./PaxHeaders/image-2.20.10000644000000000000000000000013015246255467012032 xustar0030 mtime=1788435255.248062126 29 atime=1788435255.25206223 29 ctime=1788435255.25206223 image-2.20.1/0000755000175000017500000000000015246255467014044 5ustar00avinoamavinoam00000000000000image-2.20.1/PaxHeaders/INDEX0000644000000000000000000000006115246255447012545 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/INDEX0000644000175000017500000000547315246255447014645 0ustar00avinoamavinoam00000000000000image >> Image processing Analysis and Statistics corr2 edge fftconv2 fftconvn mean2 std2 entropy entropyfilt qtdecomp qtgetblk qtsetblk graycomatrix graycomatrix_old hough houghlines houghpeaks houghtf hough_line hough_circle imbilatfilt 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 imsharpen 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 adaptthresh grayslice graythresh im2bw im2int16 im2single im2uint16 im2uint8 imbinarize 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 insertText 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.20.1/PaxHeaders/src0000644000000000000000000000013015246255467012464 xustar0030 mtime=1788435255.248062126 29 atime=1788435255.25206223 29 ctime=1788435255.25206223 image-2.20.1/src/0000755000175000017500000000000015246255467014633 5ustar00avinoamavinoam00000000000000image-2.20.1/src/PaxHeaders/Makefile.in0000644000000000000000000000006115246255447014607 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/Makefile.in0000644000175000017500000000217515246255447016703 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 \ __graycomatrix__.oct __text_to_pixels__.oct bwfill.oct \ rotate_scale.oct hough_line.oct graycomatrix_old.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.20.1/src/PaxHeaders/__spatial_filtering__.cc0000644000000000000000000000006115246255447017345 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__spatial_filtering__.cc0000644000175000017500000010423215246255447021436 0ustar00avinoamavinoam00000000000000// Copyright (C) 2008 Søren Hauberg // Copyright (C) 2013 Carnë Draug // Copyright (C) 2026 Dmitri A. Sergatskov // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 #if defined (_OPENMP) #include #endif /** * Comparator for ordered filtering. * General template uses operator<, specialized for Complex types. */ template struct value_less { static bool compare (const ET& a, const ET& b) { return a < b; } bool operator() (const ET& a, const ET& b) const { return a < b; } }; // Specialization for Complex: compare by squared magnitude. template <> struct value_less { static 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 (); return anorm2 < bnorm2; } bool operator() (const Complex& a, const Complex& b) const { return compare (a, b); } }; // Specialization for FloatComplex: compare by squared magnitude. template <> struct value_less { static bool compare (const FloatComplex& a, const FloatComplex& b) { const float anorm2 = a.real () * a.real () + a.imag () * a.imag (); const float bnorm2 = b.real () * b.real () + b.imag () * b.imag (); return anorm2 < bnorm2; } bool operator() (const FloatComplex& a, const FloatComplex& b) const { return compare (a, b); } }; /** * Filter functions for ordered filtering. */ // Select nth smallest element (0-indexed). // Uses std::nth_element for O(n) average performance. // Special cases for min/max use std::min_element/std::max_element. template ET_OUT selnth (ET* vals, octave_idx_type len, int nth) { // Special case for min: O(n) single pass if (nth == 0) return static_cast ( *std::min_element (vals, vals + len, value_less ())); // Special case for max: O(n) single pass if (nth == len - 1) return static_cast ( *std::max_element (vals, vals + len, value_less ())); // General case: use std::nth_element std::nth_element (vals, vals + nth, vals + len, value_less ()); return static_cast (vals[nth]); } // Fused gather + min for direct computation without temporary storage. // Used in 2D fast path when heights are all zero. template inline ET_OUT min_filt_gather (const ET* base, const octave_idx_type* offsets, octave_idx_type len) { ET_OUT min_val = static_cast (base[offsets[0]]); for (octave_idx_type i = 1; i < len; i++) { ET_OUT val = static_cast (base[offsets[i]]); if (value_less::compare (val, min_val)) min_val = val; } return min_val; } // Fused gather + min with heights. template inline ET_OUT min_filt_gather_h (const ET* base, const octave_idx_type* offsets, const ET* heights, octave_idx_type len) { ET_OUT min_val = static_cast (base[offsets[0]] + heights[0]); for (octave_idx_type i = 1; i < len; i++) { ET_OUT val = static_cast (base[offsets[i]] + heights[i]); if (value_less::compare (val, min_val)) min_val = val; } return min_val; } // Fused gather + max for direct computation without temporary storage. template inline ET_OUT max_filt_gather (const ET* base, const octave_idx_type* offsets, octave_idx_type len) { ET_OUT max_val = static_cast (base[offsets[0]]); for (octave_idx_type i = 1; i < len; i++) { ET_OUT val = static_cast (base[offsets[i]]); if (value_less::compare (max_val, val)) max_val = val; } return max_val; } // Fused gather + max with heights. template inline ET_OUT max_filt_gather_h (const ET* base, const octave_idx_type* offsets, const ET* heights, octave_idx_type len) { ET_OUT max_val = static_cast (base[offsets[0]] + heights[0]); for (octave_idx_type i = 1; i < len; i++) { ET_OUT val = static_cast (base[offsets[i]] + heights[i]); if (value_less::compare (max_val, val)) max_val = val; } return max_val; } // Standard min filter on pre-gathered values. template ET_OUT min_filt (ET* vals, octave_idx_type len, int) { return static_cast ( *std::min_element (vals, vals + len, value_less ())); } // Standard max filter on pre-gathered values. template ET_OUT max_filt (ET* vals, octave_idx_type len, int) { return static_cast ( *std::max_element (vals, vals + len, value_less ())); } /** * Filter functions for median filtering. */ // Average two values for even-length median. // Matches medfilt2 behavior: a./2 + b./2 (avoids overflow on integers). template inline ET_OUT median_avg (const ET_OUT& a, const ET_OUT& b) { return a / ET_OUT (2) + b / ET_OUT (2); } template <> inline double median_avg (const double& a, const double& b) { return a / 2.0 + b / 2.0; } template <> inline float median_avg (const float& a, const float& b) { return a / 2.0f + b / 2.0f; } // Median filter on pre-gathered values. // Odd length: exact middle element. // Even length: average of two middle elements (split division). template ET_OUT median_filt (ET* vals, octave_idx_type len, int) { const octave_idx_type mid_hi = len / 2; // Odd number of samples: exact median if ((len & 1) != 0) { if (mid_hi == 0) return static_cast (vals[0]); std::nth_element (vals, vals + mid_hi, vals + len, value_less ()); return static_cast (vals[mid_hi]); } // Even number of samples: // upper median is vals[mid_hi] after nth_element, // lower median is max(vals[0:mid_hi-1]). std::nth_element (vals, vals + mid_hi, vals + len, value_less ()); const ET hi = vals[mid_hi]; const ET lo = *std::max_element (vals, vals + mid_hi, value_less ()); return median_avg (static_cast (lo), static_cast (hi)); } /** * Filter functions for standard deviation filters. */ template inline ET square (const ET a) { return a * a; } template ET_OUT std_filt (ET* vals, octave_idx_type len, int norm) { ET_OUT mean = 0; for (octave_idx_type i = 0; i < len; i++) mean += static_cast (vals[i]); mean /= static_cast (len); ET_OUT var = 0; for (octave_idx_type i = 0; i < len; i++) var += square (static_cast (vals[i]) - mean); var /= static_cast (norm); return std::sqrt (var); } /** * Functions for the entropy filter. */ 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_uint8, 0, 256) #undef ENTROPY_INFO template ET_OUT entropy_filt (ET* vals, octave_idx_type len, int nbins) { ET add; get_entropy_info (add, nbins); OCTAVE_LOCAL_BUFFER (double, hist, nbins); std::fill_n (hist, nbins, 0.0); for (octave_idx_type i = 0; i < len; i++) hist[static_cast (vals[i] + add)]++; const double len_inv = 1.0 / static_cast (len); for (int i = 0; i < nbins; i++) hist[i] *= len_inv; double entropy = 0; for (int i = 0; i < nbins; i++) { const double p = hist[i]; if (p > 0) entropy -= p * std::log2 (p); } return static_cast (entropy); } /** * The function for the range filter. */ template ET_OUT range_filt (ET* vals, octave_idx_type len, int) { auto result = std::minmax_element (vals, vals + len, value_less ()); return static_cast (*result.second) - static_cast (*result.first); } /** * Filtering mode enumeration for 2D fast path. */ enum filter_mode { FILTER_MIN, FILTER_MAX, FILTER_NTH, FILTER_OTHER }; /** * The general function for doing the filtering. */ template octave_value do_filtering (const MT& in, const boolNDArray& se, ETout (*filter_function) (ET*, octave_idx_type, int), const MT& S, int arg4, filter_mode mode = FILTER_OTHER) { 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 dimensions 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); // Precompute linear strides for input array OCTAVE_LOCAL_BUFFER (octave_idx_type, in_strides, ndims); if (ndims > 0) in_strides[0] = 1; for (octave_idx_type i = 1; i < ndims; i++) in_strides[i] = in_strides[i - 1] * in_size(i - 1); // Precompute linear offsets for SE elements and extract heights OCTAVE_LOCAL_BUFFER (octave_idx_type, se_offsets, se_nnz); OCTAVE_LOCAL_BUFFER (Pin, heights, se_nnz); Array se_sub (dim_vector (ndims, 1), 0); octave_idx_type* se_sub_vec = se_sub.fortran_vec (); // Check if all heights are zero (common case with true(n,n)) bool all_heights_zero = true; octave_idx_type found = 0; const octave_idx_type se_numel = se.numel (); for (octave_idx_type i = 0; i < se_numel; i++) { if (se(se_sub)) { octave_idx_type offset = 0; for (octave_idx_type d = 0; d < ndims; d++) offset += se_sub_vec[d] * in_strides[d]; se_offsets[found] = offset; heights[found] = S(se_sub); if (heights[found] != Pin (0)) all_heights_zero = false; found++; } boolNDArray::increment_index (se_sub, se_size); } // Raw pointers for fast access const Pin* in_ptr = in.data (); ETout* out_ptr = out.fortran_vec (); const octave_idx_type* offsets_ptr = se_offsets; // --- FAST PATH FOR 2D IMAGES --- if (ndims == 2) { const octave_idx_type out_rows = out_size(0); const octave_idx_type out_cols = out_size(1); const octave_idx_type in_rows = in_size(0); #if defined (_OPENMP) // Auto-tune thread count based on workload const octave_idx_type total_ops = out_rows * out_cols * se_nnz; const int sys_max_threads = omp_get_max_threads (); int use_threads = 1; if (total_ops > 65536) { use_threads = static_cast (total_ops / 65536) + 1; if (use_threads > sys_max_threads) use_threads = sys_max_threads; } #pragma omp parallel num_threads(use_threads) #endif { // Thread-local buffer for values (only needed for non-fused paths) OCTAVE_LOCAL_BUFFER (Pin, values, se_nnz); #if defined (_OPENMP) #pragma omp for schedule(static) #endif for (octave_idx_type c = 0; c < out_cols; c++) { ETout* out_col = out_ptr + c * out_rows; const Pin* in_col = in_ptr + c * in_rows; for (octave_idx_type r = 0; r < out_rows; r++) { const Pin* pixel_base = in_col + r; // Use fused gather+filter for min/max when possible if (mode == FILTER_MIN && all_heights_zero) { out_col[r] = min_filt_gather ( pixel_base, offsets_ptr, se_nnz); } else if (mode == FILTER_MIN) { out_col[r] = min_filt_gather_h ( pixel_base, offsets_ptr, heights, se_nnz); } else if (mode == FILTER_MAX && all_heights_zero) { out_col[r] = max_filt_gather ( pixel_base, offsets_ptr, se_nnz); } else if (mode == FILTER_MAX) { out_col[r] = max_filt_gather_h ( pixel_base, offsets_ptr, heights, se_nnz); } else { // General case: gather then filter if (all_heights_zero) { #if defined (_OPENMP) #pragma omp simd #endif for (octave_idx_type k = 0; k < se_nnz; k++) values[k] = pixel_base[offsets_ptr[k]]; } else { for (octave_idx_type k = 0; k < se_nnz; k++) values[k] = pixel_base[offsets_ptr[k]] + heights[k]; } out_col[r] = filter_function (values, se_nnz, arg4); } } } } } else { // --- GENERIC N-D PATH --- OCTAVE_LOCAL_BUFFER (Pin, values, se_nnz); Array out_sub (dim_vector (ndims, 1), 0); octave_idx_type* out_sub_vec = out_sub.fortran_vec (); const octave_idx_type out_numel = out.numel (); for (octave_idx_type out_ind = 0; out_ind < out_numel; out_ind++) { // Compute base linear index in input array octave_idx_type in_base = 0; for (octave_idx_type d = 0; d < ndims; d++) in_base += out_sub_vec[d] * in_strides[d]; // Gather neighborhood values if (all_heights_zero) { for (octave_idx_type k = 0; k < se_nnz; k++) values[k] = in_ptr[in_base + offsets_ptr[k]]; } else { for (octave_idx_type k = 0; k < se_nnz; k++) values[k] = in_ptr[in_base + offsets_ptr[k]] + heights[k]; } out_ptr[out_ind] = filter_function (values, se_nnz, arg4); 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},\n\ @var{method}, @var{S}, @var{arg})\n\ \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{\"median\"}\n\ Compute the local median. The corresponding user interface is available\n\ in @code{medfilt2}.\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 (); const octave_idx_type len = domain.nnz (); // Compute a common dimensionality so that e.g. a 10x10x3 image // can be filtered with a 3x3 domain (treated as 3x3x1). const dim_vector a_dims0 = A.dims (); const dim_vector d_dims0 = domain.dims (); const dim_vector s_dims0 = S.dims (); const octave_idx_type nd = std::max ({a_dims0.ndims (), d_dims0.ndims (), s_dims0.ndims ()}); const dim_vector a_dims = a_dims0.redim (nd); const dim_vector d_dims = d_dims0.redim (nd); const dim_vector s_dims = s_dims0.redim (nd); for (octave_idx_type i = 0; i < nd; i++) { if (d_dims(i) != s_dims(i)) error ("__spatial_filtering__: DOMAIN and S must have same size"); if (a_dims(i) < d_dims(i)) error ("__spatial_filtering__: DOMAIN cannot be larger than A"); } 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, MODE) \ retval = do_filtering (A.FUN (), domain, \ FILTER_FUN, \ S.FUN (), arg4, MODE) 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; } // Determine filter mode for optimization filter_mode mode = FILTER_NTH; if (arg4 == 0) mode = FILTER_MIN; else if (arg4 == len - 1) mode = FILTER_MAX; #define ACTION(MT, FUN, ET) \ do { \ if (mode == FILTER_MIN) \ GENERAL_ACTION (MT, FUN, ET, MT, ET, min_filt, FILTER_MIN); \ else if (mode == FILTER_MAX) \ GENERAL_ACTION (MT, FUN, ET, MT, ET, max_filt, FILTER_MAX); \ else \ GENERAL_ACTION (MT, FUN, ET, MT, ET, selnth, FILTER_NTH); \ } while (0) 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, FILTER_OTHER) 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 == "median") { #define ACTION(MT, FUN, ET) \ GENERAL_ACTION (MT, FUN, ET, MT, ET, median_filt, FILTER_OTHER) 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 error ("__spatial_filtering__: A should be real, logical, 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, FILTER_OTHER) 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, FILTER_OTHER) 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.20.1/src/PaxHeaders/bwlabeln.cc0000644000000000000000000000006115246255447014637 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/bwlabeln.cc0000644000175000017500000006671015246255447016740 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.\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.20.1/src/PaxHeaders/config.h.in0000644000000000000000000000013115246255467014565 xustar0030 mtime=1788435255.246062074 30 atime=1788435255.246062074 29 ctime=1788435255.25206223 image-2.20.1/src/config.h.in0000644000175000017500000000323615246255467016662 0ustar00avinoamavinoam00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_OCTAVE_LO_MAPPERS_H /* Define to 1 if you have the header file. */ #undef HAVE_OCTAVE_MAPPERS_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS image-2.20.1/src/PaxHeaders/bwconncomp.cc0000644000000000000000000000006115246255447015216 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/bwconncomp.cc0000644000175000017500000003534415246255447017316 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 #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 (); if (args(0).is_string()) error ("bwareaopen: image BW must be a numeric or logical array"); boolNDArray BW = args(0).xbool_array_value ("bwareaopen: image BW must be a numeric or logical array"); 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.20.1/src/PaxHeaders/__text_to_pixels__.cc0000644000000000000000000000006115246255447016717 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__text_to_pixels__.cc0000644000175000017500000000337015246255447021011 0ustar00avinoamavinoam00000000000000/* Copyright (C) 2020 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 . Author: Pantxo Diribarne Created: 2020-12-04 */ #include #include DEFUN_DLD(__text_to_pixels__, args, nargout, "-*- texinfo -*-\n\ @deftypefn {} {@var{ALPHA} =} __text_to_pixels__ (@var{text}, @var{fontname}, @var{fontsize})\n\ Render @var{text} to pixels and return a 4-by-M-by-N uint8 array.\n\ @seealso{}\n\ @end deftypefn") { int nargin = args.length (); if (nargin != 3) print_usage (); std::string txt = args(0).string_value ("__text_to_pixels__:TEXT must be a string."); std::string fontname = args(1).string_value ("__text_to_pixels__:FONTNAME must be a string."); double fontsize = args(2).double_value ("__text_to_pixels__:FONTSIZE must be numeric a scalar."); octave::text_renderer txt_renderer; txt_renderer.set_font (fontname, "normal", "normal", fontsize); uint8NDArray pixels; Matrix bbox; txt_renderer.text_to_pixels (txt, pixels, bbox, /*halign*/ 0, /*valign*/ 0, /*rotation*/ 0.0, /*interpreter*/ "none"); return ovl (pixels, bbox); } image-2.20.1/src/PaxHeaders/connectivity.h0000644000000000000000000000006115246255447015431 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/connectivity.h0000644000175000017500000001246015246255447017523 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.20.1/src/PaxHeaders/strel.cc0000644000000000000000000000006115246255447014202 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/strel.cc0000644000175000017500000002341715246255447016300 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.20.1/src/PaxHeaders/__custom_gaussian_smoothing__.cc0000644000000000000000000000006115246255447021140 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__custom_gaussian_smoothing__.cc0000644000175000017500000001762315246255447023240 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.20.1/src/PaxHeaders/bwfill.cc0000644000000000000000000000006115246255447014330 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/bwfill.cc0000644000175000017500000002034715246255447016425 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.\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.20.1/src/PaxHeaders/nonmax_suppress.cc0000644000000000000000000000006115246255447016315 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/nonmax_suppress.cc0000644000175000017500000001254215246255447020410 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.20.1/src/PaxHeaders/imreconstruct.cc0000644000000000000000000000006115246255447015752 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/imreconstruct.cc0000644000175000017500000004121415246255447020043 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 "config.h" #include #include // Octave 11 renamed lo-mappers to mappers so try that first to avoid // deprecation warning. Drop this once we are dependent on Octave 11. // See bug #68028. #if defined (HAVE_OCTAVE_MAPPERS_H) #include #else #include #endif #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\ Perform the morpological reconstruction of a given image. \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.20.1/src/PaxHeaders/strel.h0000644000000000000000000000006115246255447014044 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/strel.h0000644000175000017500000001042115246255447016131 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.20.1/src/PaxHeaders/__eps__.cc0000644000000000000000000000006115246255447014434 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__eps__.cc0000644000175000017500000000521615246255447016527 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 "config.h" #include #include #include // Octave 11 renamed lo-mappers to mappers so try that first to avoid // deprecation warning. Drop this once we are dependent on Octave 11. // See bug #68028. #if defined (HAVE_OCTAVE_MAPPERS_H) #include #else #include #endif // 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.20.1/src/PaxHeaders/rotate_scale.cc0000644000000000000000000000006115246255447015516 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/rotate_scale.cc0000644000175000017500000001340715246255447017612 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.20.1/src/PaxHeaders/watershed.cc0000644000000000000000000000006115246255447015037 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/watershed.cc0000644000175000017500000004537015246255447017137 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.20.1/src/PaxHeaders/configure0000644000000000000000000000013115246255467014446 xustar0030 mtime=1788435255.131059079 30 atime=1788435255.111058559 29 ctime=1788435255.25206223 image-2.20.1/src/configure0000755000175000017500000035323415246255467016554 0ustar00avinoamavinoam00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Octave-Forge image package 2.20.1. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 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 as_nop=: if test ${ZSH_VERSION+y} && (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 $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; 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 # 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 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="as_nop=: if test \${ZSH_VERSION+y} && (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 \$as_nop 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 \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || 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_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi 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'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$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_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # 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=`printf "%s\n" "$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 || printf "%s\n" 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_nop 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_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # 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 printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$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 || printf "%s\n" 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" || { printf "%s\n" "$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 } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. 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 # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' 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.20.1' PACKAGE_STRING='Octave-Forge image package 2.20.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_cxx_list= 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 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=`printf "%s\n" "$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=`printf "%s\n" "$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=`printf "%s\n" "$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=`printf "%s\n" "$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. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$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" ;; *) printf "%s\n" "$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 || printf "%s\n" 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.20.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.20.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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$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 configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. 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 printf "%s\n" "$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.20.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 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 conftest.beam 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\"" printf "%s\n" "$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 printf "%s\n" "$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_nop printf "%s\n" "$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 # ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_cxx_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_compile ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac 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.20.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "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=`printf "%s\n" "$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=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## 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_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$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 printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$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 printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C++ compiler supports C++98 (global declarations) ac_cxx_conftest_cxx98_globals=' // Does the compiler advertise C++98 conformance? #if !defined __cplusplus || __cplusplus < 199711L # error "Compiler does not advertise C++98 conformance" #endif // These inclusions are to reject old compilers that // lack the unsuffixed header files. #include #include // and are *not* freestanding headers in C++98. extern void assert (int); namespace std { extern int strcmp (const char *, const char *); } // Namespaces, exceptions, and templates were all added after "C++ 2.0". using std::exception; using std::strcmp; namespace { void test_exception_syntax() { try { throw "test"; } catch (const char *s) { // Extra parentheses suppress a warning when building autoconf itself, // due to lint rules shared with more typical C programs. assert (!(strcmp) (s, "test")); } } template struct test_template { T const val; explicit test_template(T t) : val(t) {} template T add(U u) { return static_cast(u) + val; } }; } // anonymous namespace ' # Test code for whether the C++ compiler supports C++98 (body of main) ac_cxx_conftest_cxx98_main=' assert (argc); assert (! argv[0]); { test_exception_syntax (); test_template tt (2.0); assert (tt.add (4) == 6.0); assert (true && !false); } ' # Test code for whether the C++ compiler supports C++11 (global declarations) ac_cxx_conftest_cxx11_globals=' // Does the compiler advertise C++ 2011 conformance? #if !defined __cplusplus || __cplusplus < 201103L # error "Compiler does not advertise C++11 conformance" #endif namespace cxx11test { constexpr int get_val() { return 20; } struct testinit { int i; double d; }; class delegate { public: delegate(int n) : n(n) {} delegate(): delegate(2354) {} virtual int getval() { return this->n; }; protected: int n; }; class overridden : public delegate { public: overridden(int n): delegate(n) {} virtual int getval() override final { return this->n * 2; } }; class nocopy { public: nocopy(int i): i(i) {} nocopy() = default; nocopy(const nocopy&) = delete; nocopy & operator=(const nocopy&) = delete; private: int i; }; // for testing lambda expressions template Ret eval(Fn f, Ret v) { return f(v); } // for testing variadic templates and trailing return types template auto sum(V first) -> V { return first; } template auto sum(V first, Args... rest) -> V { return first + sum(rest...); } } ' # Test code for whether the C++ compiler supports C++11 (body of main) ac_cxx_conftest_cxx11_main=' { // Test auto and decltype auto a1 = 6538; auto a2 = 48573953.4; auto a3 = "String literal"; int total = 0; for (auto i = a3; *i; ++i) { total += *i; } decltype(a2) a4 = 34895.034; } { // Test constexpr short sa[cxx11test::get_val()] = { 0 }; } { // Test initializer lists cxx11test::testinit il = { 4323, 435234.23544 }; } { // Test range-based for int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, 14, 19, 17, 8, 6, 20, 16, 2, 11, 1}; for (auto &x : array) { x += 23; } } { // Test lambda expressions using cxx11test::eval; assert (eval ([](int x) { return x*2; }, 21) == 42); double d = 2.0; assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0); assert (d == 5.0); assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0); assert (d == 5.0); } { // Test use of variadic templates using cxx11test::sum; auto a = sum(1); auto b = sum(1, 2); auto c = sum(1.0, 2.0, 3.0); } { // Test constructor delegation cxx11test::delegate d1; cxx11test::delegate d2(); cxx11test::delegate d3(45); } { // Test override and final cxx11test::overridden o1(55464); } { // Test nullptr char *c = nullptr; } { // Test template brackets test_template<::test_template> v(test_template(12)); } { // Unicode literals char const *utf8 = u8"UTF-8 string \u2500"; char16_t const *utf16 = u"UTF-8 string \u2500"; char32_t const *utf32 = U"UTF-32 string \u2500"; } ' # Test code for whether the C compiler supports C++11 (complete). ac_cxx_conftest_cxx11_program="${ac_cxx_conftest_cxx98_globals} ${ac_cxx_conftest_cxx11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_cxx_conftest_cxx98_main} ${ac_cxx_conftest_cxx11_main} return ok; } " # Test code for whether the C compiler supports C++98 (complete). ac_cxx_conftest_cxx98_program="${ac_cxx_conftest_cxx98_globals} int main (int argc, char **argv) { int ok = 0; ${ac_cxx_conftest_cxx98_main} return ok; } " as_fn_append ac_header_cxx_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_cxx_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_cxx_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_cxx_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_cxx_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_cxx_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_cxx_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_cxx_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_cxx_list " unistd.h unistd_h HAVE_UNISTD_H" # 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,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$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=`printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_MKOCTFILE+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKOCTFILE" >&5 printf "%s\n" "$MKOCTFILE" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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` `${MKOCTFILE} -p INCFLAGS`" ## Octave 11 renamed lo-mappers.h to mappers.h so check what we have. ## Drop this once we are dependent on Octave 11. See bug #68028. 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 clang++ 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CXX+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 printf "%s\n" "$CXX" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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 clang++ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CXX+y} then : printf %s "(cached) " >&6 else $as_nop 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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" printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 printf "%s\n" "$ac_ct_CXX" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "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:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$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. printf "%s\n" "$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\"" printf "%s\n" "$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 printf "%s\n" "$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 (void) { ; 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 printf %s "checking whether the C++ compiler works... " >&6; } ac_link_default=`printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$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+y} && 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 $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 printf %s "checking for C++ compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$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 (void) { 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. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$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 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$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_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5 printf %s "checking whether the compiler supports GNU C++... " >&6; } if test ${ac_cv_cxx_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+y} ac_save_CXXFLAGS=$CXXFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 printf %s "checking whether $CXX accepts -g... " >&6; } if test ${ac_cv_prog_cxx_g+y} then : printf %s "(cached) " >&6 else $as_nop 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 (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_g=yes else $as_nop CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : else $as_nop ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; 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.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 printf "%s\n" "$ac_cv_prog_cxx_g" >&6; } if test $ac_test_CXXFLAGS; 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_prog_cxx_stdcxx=no if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5 printf %s "checking for $CXX option to enable C++11 features... " >&6; } if test ${ac_cv_prog_cxx_11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cxx_11=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_cxx_conftest_cxx11_program _ACEOF for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA do CXX="$ac_save_CXX $ac_arg" if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_cxx11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cxx_cxx11" != "xno" && break done rm -f conftest.$ac_ext CXX=$ac_save_CXX fi if test "x$ac_cv_prog_cxx_cxx11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cxx_cxx11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5 printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } CXX="$CXX $ac_cv_prog_cxx_cxx11" fi ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11 ac_prog_cxx_stdcxx=cxx11 fi fi if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5 printf %s "checking for $CXX option to enable C++98 features... " >&6; } if test ${ac_cv_prog_cxx_98+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cxx_98=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_cxx_conftest_cxx98_program _ACEOF for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA do CXX="$ac_save_CXX $ac_arg" if ac_fn_cxx_try_compile "$LINENO" then : ac_cv_prog_cxx_cxx98=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cxx_cxx98" != "xno" && break done rm -f conftest.$ac_ext CXX=$ac_save_CXX fi if test "x$ac_cv_prog_cxx_cxx98" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cxx_cxx98" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5 printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } CXX="$CXX $ac_cv_prog_cxx_cxx98" fi ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98 ac_prog_cxx_stdcxx=cxx98 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 ac_header= ac_cache= for ac_item in $ac_header_cxx_list do if test $ac_cache; then ac_fn_cxx_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi for ac_header in octave/mappers.h do : ac_fn_cxx_check_header_compile "$LINENO" "octave/mappers.h" "ac_cv_header_octave_mappers_h" "$ac_includes_default" if test "x$ac_cv_header_octave_mappers_h" = xyes then : printf "%s\n" "#define HAVE_OCTAVE_MAPPERS_H 1" >>confdefs.h else $as_nop for ac_header in octave/lo-mappers.h do : ac_fn_cxx_check_header_compile "$LINENO" "octave/lo-mappers.h" "ac_cv_header_octave_lo_mappers_h" "$ac_includes_default" if test "x$ac_cv_header_octave_lo_mappers_h" = xyes then : printf "%s\n" "#define HAVE_OCTAVE_LO_MAPPERS_H 1" >>confdefs.h else $as_nop as_fn_error $? "Failed to find octave's mappers header file" "$LINENO" 5 fi done fi done ## 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether templated lambda functions accept '&const int'" >&5 printf %s "checking whether templated lambda functions accept '&const int'... " >&6; } if test ${_cv_template_lambda_accepts_ref_const_inst+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ template void test (T b) { const int a = b; [&] () { return a, a; }(); } int main (void) { test (1); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO" then : _cv_template_lambda_accepts_ref_const_inst=yes else $as_nop _cv_template_lambda_accepts_ref_const_inst=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $_cv_template_lambda_accepts_ref_const_inst" >&5 printf "%s\n" "$_cv_template_lambda_accepts_ref_const_inst" >&6; } if test $_cv_template_lambda_accepts_ref_const_inst = no; then { printf "%s\n" "$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 printf "%s\n" "$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_headers="$ac_config_headers config.h:config.h.in" 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_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$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+y} || &/ 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$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}' DEFS=-DHAVE_CONFIG_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=`printf "%s\n" "$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" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$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 as_nop=: if test ${ZSH_VERSION+y} && (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 $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; 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 # 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 case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac 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 printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # 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 printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$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_nop 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_nop 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 || printf "%s\n" 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 # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. 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 # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' 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=`printf "%s\n" "$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 || printf "%s\n" 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.20.1, which was generated by GNU Autoconf 2.71. 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 case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _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 --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ Octave-Forge image package config.status 2.20.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 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 ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$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 \printf "%s\n" "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 printf "%s\n" "$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 "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h:config.h.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+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers 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" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " 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=`printf "%s\n" "$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 '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$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 || printf "%s\n" 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=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$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@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$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"; } && { printf "%s\n" "$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 printf "%s\n" "$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 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; 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 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi image-2.20.1/src/PaxHeaders/hough_line.cc0000644000000000000000000000006115246255447015172 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/hough_line.cc0000644000175000017500000000742315246255447017267 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.20.1/src/PaxHeaders/union-find.h0000644000000000000000000000006115246255447014761 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/union-find.h0000644000175000017500000000457215246255447017060 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.20.1/src/PaxHeaders/graycomatrix_old.cc0000644000000000000000000000006115246255447016420 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/graycomatrix_old.cc0000644000175000017500000001151715246255447020514 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_old, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {@var{P} =} graycomatrix_old(@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_old(a, 3, 1, -pi/4)) == [4 2 0; %! 2 3 2; %! 1 2 0]; %! %!assert(size(graycomatrix_old(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_old(a, 3, 1, [0 1]*-pi/4) %! */ image-2.20.1/src/PaxHeaders/__graycomatrix__.cc0000644000000000000000000000006115246255447016356 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__graycomatrix__.cc0000644000175000017500000000751115246255447020451 0ustar00avinoamavinoam00000000000000// Copyright (C) 2025 Avinam Kalma // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General 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 DEFUN_DLD (__graycomatrix__, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} __graycomatrix__(@var{img}, @var{offset}, @var{num_levels}, @var{symmetric})\n\ Compute the gray-level co-occurrence matrix (GLCM) for an image\n\ \n\ @var{img} is a 2D gray-level image.\n\ \n\ @var{offset} is an M-by-2 array specifying the pixel pair offsets for which the GLCM is calculated.\n\ \n\ @var{num_levels} is a scalar specifying the number of gray levels to use when scaling the input image.\n\ \n\ @var{symmetric} is a logical value indicating whether the GLCM should be symmetric.\n\ \n\ This function is internal and should NOT be called directly. Instead use @code{graycomatrix}.\n\ @end deftypefn\n\ ") { // check number of inputs if (args.length() != 4) print_usage(); Matrix scaled_image = args(0).array_value(); Matrix offset = args(1).array_value(); int num_levels = args(2).int_value(); bool symmetric = args(3).bool_value(); // dimensions octave_idx_type rows = scaled_image.rows(); octave_idx_type cols = scaled_image.columns(); octave_idx_type n_offsets = offset.rows(); // prepare output: 3D array flattened in row-major for octave // Create output GLCM NDArray glcm(dim_vector(num_levels, num_levels, n_offsets)); glcm.fill(0.0); // pointers for speed double* image_ptr = scaled_image.fortran_vec(); // column-major double* glcm_ptr = glcm.fortran_vec(); // Edge handling: c_min, c_max, r_min, r_max ensure no out-of-bounds access // for each offset, compute valid pixel ranges for (octave_idx_type k = 0; k < n_offsets; ++k) { int dy = (int)offset(k, 0); int dx = (int)offset(k, 1); int k_offset = k * num_levels * num_levels; int c_min = std::max(0, -dx); int c_max = std::min(cols, cols - dx); int r_min = std::max(0, -dy); int r_max = std::min(rows, rows - dy); for (octave_idx_type c = c_min; c < c_max; ++c) { int c2 = c + dx; int crows = c * rows; int crows2 = c2 * rows; int row_index1 = r_min + crows; int row_index2 = r_min + dy + crows2; for (octave_idx_type r = r_min; r < r_max; ++r) { // NaN handling: skip if either pixel is NaN if (std::isnan(image_ptr[row_index1]) || std::isnan(image_ptr[row_index2])) { row_index1++; row_index2++; continue; } int gray_level1 = (int)image_ptr[row_index1++]; int gray_level2 = (int)image_ptr[row_index2++]; // linear index in 3D: // gray_level1 + gray_level2*num_levels + k*num_levels*num_levels glcm_ptr[gray_level1 + gray_level2 * num_levels + k_offset] += 1.0; } } // Symmetric block: mirror GLCM for each offset if (symmetric) { for (octave_idx_type i = 0; i < num_levels; ++i) { for (octave_idx_type j = 0; j <= i; ++j) { glcm(i, j, k) += glcm(j, i, k); glcm(j, i, k) = glcm(i, j, k); } } } } // end of loop on offsets // Return: output GLCM array return octave_value(glcm); } image-2.20.1/src/PaxHeaders/__bilateral__.cc0000644000000000000000000000006115246255447015604 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__bilateral__.cc0000644000175000017500000001352215246255447017676 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, const int s) { // 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 // old code // const int s = std::max ((int)std::round (3*sigma_d), 1); const int s21 = 2*s+1; Matrix kernel (s21, s21); double sigma_d2 = -0.5 / (sigma_d * sigma_d); for (octave_idx_type r = 0; r < s21; r++) { const int dr = r-s; const int dr2 = dr*dr; for (octave_idx_type c = 0; c < s21; c++) { const int dc = c-s; kernel (r,c) = exp ((dr2 + dc*dc)*sigma_d2); } } // 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 < s21; kr++) { for (octave_idx_type kc = 0; kc < s21; 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}, @var{half_kernel_size})\n\ Performs Gaussian bilateral filtering in the image @var{im}.\n\ @var{sigma_d} is the spread of the Gaussian used as closenes function,\n\ and @var{sigma_r} is the spread of Gaussian used as similarity function.\n\ @var{half_kernel_size} is half of the kernel size used.\n\ \n\ This function is internal and should NOT be called directly. Instead use @code{imsmooth}.\n\ @end deftypefn\n\ ") { octave_value_list retval; if (args.length () != 4) 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 (); const int half_size = args (3).int_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, half_size); } else if (args (0).is_int8_type ()) { const int8NDArray im = args (0).int8_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args (0).is_int16_type ()) { const int16NDArray im = args (0).int16_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args (0).is_int32_type ()) { const int32NDArray im = args (0).int32_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args (0).is_int64_type ()) { const int64NDArray im = args (0).int64_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args (0).is_uint8_type ()) { const uint8NDArray im = args (0).uint8_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args(0).is_uint16_type()) { const uint16NDArray im = args (0).uint16_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args (0).is_uint32_type ()) { const uint32NDArray im = args (0).uint32_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else if (args (0).is_uint64_type ()) { const uint64NDArray im = args (0).uint64_array_value (); retval = bilateral (im, sigma_d, sigma_r, half_size); } else error ("__bilateral__: first input should be a real or integer array"); return retval; } image-2.20.1/src/PaxHeaders/conndef.cc0000644000000000000000000000006115246255447014465 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/conndef.cc0000644000175000017500000002223515246255447016560 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.20.1/src/PaxHeaders/__boundary__.cc0000644000000000000000000000006115246255447015470 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/__boundary__.cc0000644000175000017500000001264115246255447017563 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.20.1/src/PaxHeaders/bwdist.cc0000644000000000000000000000006115246255447014345 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/bwdist.cc0000644000175000017500000006521315246255447016443 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.20.1/src/PaxHeaders/intlut.cc0000644000000000000000000000006115246255447014370 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/intlut.cc0000644000175000017500000001071115246255447016457 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.20.1/src/PaxHeaders/configure.ac0000644000000000000000000000007315246255447015033 xustar0030 atime=1788435253.606019277 29 ctime=1788435255.25206223 image-2.20.1/src/configure.ac0000644000175000017500000000410415246255447017116 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.20.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` `${MKOCTFILE} -p INCFLAGS`" ## Octave 11 renamed lo-mappers.h to mappers.h so check what we have. ## Drop this once we are dependent on Octave 11. See bug #68028. AC_CHECK_HEADERS([octave/mappers.h], [], [AC_CHECK_HEADERS( [octave/lo-mappers.h], [], AC_MSG_ERROR([Failed to find octave's mappers header file]))]) ## 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_HEADERS([config.h:config.h.in]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT image-2.20.1/src/PaxHeaders/connectivity.cc0000644000000000000000000000006115246255447015567 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/connectivity.cc0000644000175000017500000002217115246255447017661 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.20.1/src/PaxHeaders/imerode.cc0000644000000000000000000000006115246255447014475 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/src/imerode.cc0000644000175000017500000010304015246255447016562 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.20.1/PaxHeaders/DESCRIPTION0000644000000000000000000000006115246255447013461 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/DESCRIPTION0000644000175000017500000000101615246255447015546 0ustar00avinoamavinoam00000000000000Name: image Version: 2.20.1 Date: 2026-09-03 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 (>= 8.1) License: GPLv3+ Url: http://octave.sf.net image-2.20.1/PaxHeaders/NEWS0000644000000000000000000000006115246255447012452 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/NEWS0000644000175000017500000011534515246255447014552 0ustar00avinoamavinoam00000000000000 Summary of important user-visible changes for image 2.20.1 (2026/09/03): ------------------------------------------------------------------------- ** image 2.20.1 is a bug fix release. ** Improve image spatial filters for higher-dimensional inputs (bug #45088). ** edge: Allow binary input image for edge (bug #55432) ** mormxcorr2: re-write the code and optimize it to make it compatible with MATLAB (bug #50151) Summary of important user-visible changes for image 2.20.0 (2026/03/11): ------------------------------------------------------------------------- ** Octave version 8.1.0 is now the minimum requirement, since imtranslate uses interp2 which was fixed in this version. ** imtranslate was re-written to be MATLAB compatible (bug #50257). The previous version was renamed to imtranslate_old. ** bootstrap: use autoreconf instead of autoconf to generate config.h.in automatically (bug #68028). ** Optimize __spatial_filtering__.cc for performance (bug #67989). ** Fix handling NaN values graycomatrix (bug #67905). ** imhist: fix the call to colorbar because of its API change (bug #68089). Summary of important user-visible changes for image 2.18.2 (2026/02/15): ------------------------------------------------------------------------- ** image 2.18.2 is a patch release. ** Fix a bug in imtransform, which wasn't MATLAB compatible for linear and cubic interpolation (bug #67682). ** Change #include to #include (bug #68028). ** Check explicitly if input is a string in bwconncomp.cc (bug #67895). Summary of important user-visible changes for image 2.18.1 (2025/09/05): ------------------------------------------------------------------------- ** image 2.18.1 is a patch release. ** Improve doc string for some functions. ** Fix typos in documentation. ** Remove trailing spaces. Summary of important user-visible changes for image 2.18.0 (2025/08/19): ------------------------------------------------------------------------- ** Octave version 7.2.0 is now the minimum requirement, since validateattribute, which is used in some new functions, fails on 7.1.0. ** graycomatrix has been rewritten to be MATLAB compatible. The previous version was renamed to graycomatrix_old (bug #38087). ** Fixing filter implementation in iradon (bug #59773). ** Add Lancsoz2 & Lancsoz3 interpolation for imresize (bug #62099). ** Fix (again) imresize when there is anti-aliasing by adding weight normalization (bug #63560). ** Fix Makefile so that it will not use GNU extensions for grep (bug #66829). ** The following functions are new: adaptthresh imbilatfilt imbinarize insertText 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.20.1/PaxHeaders/io.sourceforge.octave.image.metainfo.xml0000644000000000000000000000006115246255447021570 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/io.sourceforge.octave.image.metainfo.xml0000644000175000017500000000303215246255447023655 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 image-2.20.1/PaxHeaders/COPYING0000644000000000000000000000006115246255447013006 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/COPYING0000644000175000017500000010451315246255447015101 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.20.1/PaxHeaders/run_pkg_add.m0000644000000000000000000000006115246255447014406 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/run_pkg_add.m0000644000175000017500000000554315246255447016504 0ustar00avinoamavinoam00000000000000## Copyright (C) 2025 Dmitri A. Sergatskov ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License 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} {} run_pkg_add () ## @deftypefnx {Function File} {} run_pkg_add (@var{dirs}) ## Process PKG_ADD directives in source files. ## ## This function searches for PKG_ADD directives in .m and .cc files ## in the specified directories and evaluates them. ## ## If no @var{dirs} are provided, it searches in "inst" and "src" directories. ## ## @end deftypefn function run_pkg_add (dirs) # If no directories are provided, use "inst" and "src" if (nargin < 1) dirs = {"inst", "src"}; endif # Process each directory for i = 1:length (dirs) d = dirs{i}; if (exist (d, "dir")) # Process .m files mfiles = dir (fullfile (d, "*.m")); for j = 1:length (mfiles) if (! mfiles(j).isdir) process_file (fullfile (d, mfiles(j).name)); endif endfor # Process .cc files ccfiles = dir (fullfile (d, "*.cc")); for j = 1:length (ccfiles) if (! ccfiles(j).isdir) process_file (fullfile (d, ccfiles(j).name)); endif endfor # *.cc files endif endfor # dirs endfunction function process_file (filename) # Open the file fid = fopen (filename, "r"); if (fid < 0) return; endif unwind_protect # Read line by line, looking for PKG_ADD directives while (! feof (fid)) line = fgetl (fid); if (! ischar (line)) break; endif # Check for PKG_ADD lines using string functions if (strncmp (line, "// PKG_ADD:", 11)) # Extract the directive pkg_cmd = line(12:end); elseif (strncmp (line, "## PKG_ADD:", 11)) # Extract the directive pkg_cmd = line(12:end); else continue; endif # Trim leading/trailing whitespace pkg_cmd = strtrim (pkg_cmd); # Evaluate the directive if (! isempty (pkg_cmd)) try eval (pkg_cmd); catch err warning (["Error evaluating PKG_ADD directive from ", ... filename, ": ", err.message]); end_try_catch endif endwhile unwind_protect_cleanup # Close the file fclose (fid); end_unwind_protect endfunction image-2.20.1/PaxHeaders/inst0000644000000000000000000000013015246255465012650 xustar0030 mtime=1788435253.505016637 29 atime=1788435255.25206223 29 ctime=1788435255.25206223 image-2.20.1/inst/0000755000175000017500000000000015246255465015017 5ustar00avinoamavinoam00000000000000image-2.20.1/inst/PaxHeaders/fchcode.m0000644000000000000000000000006115246255447014501 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/fchcode.m0000644000175000017500000000472215246255447016575 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.20.1/inst/PaxHeaders/lab2uint8.m0000644000000000000000000000006115246255447014716 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/lab2uint8.m0000644000175000017500000000525415246255447017013 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.20.1/inst/PaxHeaders/edgetaper.m0000644000000000000000000000006115246255447015046 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/edgetaper.m0000644000175000017500000001041115246255447017132 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.20.1/inst/PaxHeaders/otf2psf.m0000644000000000000000000000006115246255447014471 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/otf2psf.m0000644000175000017500000000764315246255447016572 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.20.1/inst/PaxHeaders/fspecial.m0000644000000000000000000000006115246255447014674 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/fspecial.m0000644000175000017500000005046615246255447016776 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.20.1/inst/PaxHeaders/imtranslate_old.m0000644000000000000000000000006115246255447016267 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imtranslate_old.m0000644000175000017500000000512715246255447020363 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_old (@var{M}, @var{x}, @var{y}) ## @deftypefnx {Function File} {@var{Y}} = imtranslate_old (@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_old (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_old (ones (5, 5), 2, 1, "crop"); %! exp = zeros (5, 5); %! exp(1:4, 3:5) = 1; %! assert (obs, exp, eps * 10) %! %! obs = imtranslate_old (ones (5, 5), -2, -1, "crop"); %! exp = zeros (5, 5); %! exp(2:5, 1:3) = 1; %! assert (obs, exp, eps * 10) image-2.20.1/inst/PaxHeaders/imclose.m0000644000000000000000000000006115246255447014541 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imclose.m0000644000175000017500000001114715246255447016634 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.20.1/inst/PaxHeaders/imshowpair.m0000644000000000000000000000006115246255447015270 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imshowpair.m0000644000175000017500000002203615246255447017362 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 {Function File} {@var{c} =} imshowpair (@var{a}, @var{b}) ## @deftypefnx {Function File} {[@var{c}, @var{rc}] =} imshowpair (@var{a}, @var{ra}, @var{b}, @var{rb}) ## @deftypefnx {Function File} {@var{c} =} imshowpair (@dots{}, @var{method}) ## @deftypefnx {Function File} {@var{c} =} imshowpair (@dots{}, @var{name}, @var{value}) ## ## Combines two images using a specified 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.20.1/inst/PaxHeaders/@strel0000644000000000000000000000013015246255465014101 xustar0030 mtime=1788435253.447015122 29 atime=1788435255.25206223 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/0000755000175000017500000000000015246255465016250 5ustar00avinoamavinoam00000000000000image-2.20.1/inst/@strel/PaxHeaders/isflat.m0000644000000000000000000000006115246255447015621 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/isflat.m0000644000175000017500000000232215246255447017707 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.20.1/inst/@strel/PaxHeaders/isscalar.m0000644000000000000000000000006115246255447016140 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/isscalar.m0000644000175000017500000000243315246255447020231 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.20.1/inst/@strel/PaxHeaders/size.m0000644000000000000000000000006115246255447015311 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/size.m0000644000175000017500000000241615246255447017403 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.20.1/inst/@strel/PaxHeaders/getnhood.m0000644000000000000000000000006115246255447016146 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/getnhood.m0000644000175000017500000000171115246255447020235 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 a given structuring element. ## ## @seealso{getneighbors, strel} ## @end deftypefn function NHOOD = getnhood (SE) NHOOD = SE.nhood; endfunction image-2.20.1/inst/@strel/PaxHeaders/getheight.m0000644000000000000000000000006115246255447016307 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/getheight.m0000644000175000017500000000205115246255447020374 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.20.1/inst/@strel/PaxHeaders/numel.m0000644000000000000000000000006115246255447015457 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/numel.m0000644000175000017500000000202615246255447017546 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.20.1/inst/@strel/PaxHeaders/reflect.m0000644000000000000000000000006115246255447015763 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/reflect.m0000644000175000017500000000501015246255447020046 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.20.1/inst/@strel/PaxHeaders/subsref.m0000644000000000000000000000006115246255447016010 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/subsref.m0000644000175000017500000000330315246255447020076 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.20.1/inst/@strel/PaxHeaders/display.m0000644000000000000000000000006115246255447016004 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/display.m0000644000175000017500000000275415246255447020103 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.20.1/inst/@strel/PaxHeaders/getneighbors.m0000644000000000000000000000006115246255447017017 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/getneighbors.m0000644000175000017500000000375615246255447021121 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.20.1/inst/@strel/PaxHeaders/strel.m0000644000000000000000000000006115246255447015470 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/strel.m0000644000175000017500000006153615246255447017572 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.20.1/inst/@strel/PaxHeaders/translate.m0000644000000000000000000000006115246255447016334 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/translate.m0000644000175000017500000000223115246255447020421 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.20.1/inst/@strel/PaxHeaders/getsequence.m0000644000000000000000000000006115246255447016647 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@strel/getsequence.m0000644000175000017500000000521115246255447020735 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.20.1/inst/PaxHeaders/imfuse.m0000644000000000000000000000006115246255447014376 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imfuse.m0000644000175000017500000006433215246255447016475 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} {@var{c} =} imfuse (@var{a}, @var{b}) ## @deftypefnx {Function File} {[@var{c}, @var{rc}] =} imfuse (@var{a}, @var{ra}, @var{b}, @var{rb}) ## @deftypefnx {Function File} {@var{c} =} imfuse (@dots{}, @var{method}) ## @deftypefnx {Function File} {@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.20.1/inst/PaxHeaders/viscircles.m0000644000000000000000000000006115246255447015254 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/viscircles.m0000644000175000017500000001475415246255447017356 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.20.1/inst/PaxHeaders/bwpack.m0000644000000000000000000000006115246255447014355 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/bwpack.m0000644000175000017500000000634415246255447016453 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.20.1/inst/PaxHeaders/imregionalmax.m0000644000000000000000000000006115246255447015742 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imregionalmax.m0000644000175000017500000000630615246255447020036 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.20.1/inst/PaxHeaders/phantom.m0000644000000000000000000000006115246255447014554 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/phantom.m0000644000175000017500000002041415246255447016644 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.20.1/inst/PaxHeaders/affine3d.m0000644000000000000000000000006115246255447014565 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/affine3d.m0000644000175000017500000001363715246255447016666 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.20.1/inst/PaxHeaders/imbothat.m0000644000000000000000000000006115246255447014715 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imbothat.m0000644000175000017500000001574515246255447017020 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.20.1/inst/PaxHeaders/iradon.m0000644000000000000000000000006115246255447014362 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/iradon.m0000644000175000017500000002416415246255447016460 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 projections ## 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 || isempty (scaling)) scaling = 1; endif if (nargin < 4 || isempty (filter)) filter = "Ram-Lak"; endif if (nargin < 3 || isempty (interp)) interp = "linear"; endif if (nargin < 2 || isempty (theta)) 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 valid_interpolations = {"nearest", "linear", "spline", "pchip", "cubic"}; if (!any (strcmpi (interp, valid_interpolations))) error ("iradon: Invalid interpolation method specified. Supported methods: %s", strjoin(valid_interpolations, ", ")); endif ## Convert angles to radians theta *= pi / 180; ## Filter the projections [filtered, filt] = rho_filter (proj, filter, scaling); ## Perform back-projection recon = back_project (filtered, theta, interp, output_size); endfunction function recon = back_project (proj, theta, interpolation, dim) ## Create an empty image recon = zeros (dim, dim); ## Zero pad projections if the image diagonal exceeds projection size diagonal = ceil (dim * sqrt (2)) + 1; if (size (proj, 1) < diagonal) padding = ceil ((diagonal - size (proj, 1)) / 2); proj = padarray (proj, padding); 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_expected = 0.4671 .* ones (2); %! assert (A, A_expected, 0.0001); # 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_expected = [1, 1, 1, 1]' * [0.4264, 2.7859, 2.7152, 0.3557]; %! assert (A, A_expected, 0.00005); ## 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_expected = 0.3601; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 0.0218; %! assert (meandiff, meandiff_expected, 0.00005); %! %! filtername = "None"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_expected = 36.5671; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 24.6302; %! assert (meandiff, meandiff_expected, 0.00005); %! %! filtername = "Ram-Lak"; # is same as default %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_expected = 0.3601; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 0.0218; %! assert (meandiff, meandiff_expected, 0.00005); %! %! filtername = "Hamming"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_expected = 0.5171; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 0.0278; %! assert (meandiff, meandiff_expected, 0.00005); %! %! filtername = "Shepp-Logan"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_expected = 0.3941; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 0.0226; %! assert (meandiff, meandiff_expected, 0.00005); %! %! filtername = "Cosine"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_expected = 0.4681; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 0.0249; %! assert (meandiff, meandiff_expected, 0.00005); %! %! filtername = "Hann"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_expected = 0.5334; %! assert (maxdiff, maxdiff_expected, 0.00005); %! meandiff = mean (abs (D(:))); %! meandiff_expected = 0.0285; %! assert (meandiff, meandiff_expected, 0.00005); %!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.20.1/inst/PaxHeaders/immultiply.m0000644000000000000000000000006115246255447015313 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/immultiply.m0000644000175000017500000000532215246255447017404 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 a 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.20.1/inst/PaxHeaders/poly2mask.m0000644000000000000000000000006115246255447015027 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/poly2mask.m0000644000175000017500000001553615246255447017130 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.20.1/inst/PaxHeaders/integralImage.m0000644000000000000000000000006115246255447015656 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/integralImage.m0000644000175000017500000001265015246255447017751 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.20.1/inst/PaxHeaders/isrgb.m0000644000000000000000000000006115246255447014214 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/isrgb.m0000644000175000017500000000477215246255447016315 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 a given image 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.20.1/inst/PaxHeaders/imrotate.m0000644000000000000000000000006115246255447014732 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imrotate.m0000644000175000017500000003463315246255447017032 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.20.1/inst/PaxHeaders/isgray.m0000644000000000000000000000006115246255447014404 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/isgray.m0000644000175000017500000000554515246255447016504 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 a given image 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.20.1/inst/PaxHeaders/imclearborder.m0000644000000000000000000000006115246255447015720 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imclearborder.m0000644000175000017500000001150715246255447020013 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.20.1/inst/PaxHeaders/xyz2rgb.m0000644000000000000000000000006115246255447014515 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/xyz2rgb.m0000644000175000017500000001117415246255447016610 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.20.1/inst/PaxHeaders/imremap.m0000644000000000000000000000006115246255447014540 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imremap.m0000644000175000017500000002600615246255447016633 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 a given image. ## ## 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.20.1/inst/PaxHeaders/mmgradm.m0000644000000000000000000000006115246255447014532 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/mmgradm.m0000644000175000017500000000532515246255447016626 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.20.1/inst/PaxHeaders/imadjust.m0000644000000000000000000000006115246255447014726 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imadjust.m0000644000175000017500000004511415246255447017022 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.20.1/inst/PaxHeaders/grayslice.m0000644000000000000000000000006115246255447015070 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/grayslice.m0000644000175000017500000002665515246255447017175 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.20.1/inst/PaxHeaders/otsuthresh.m0000644000000000000000000000006115246255447015316 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/otsuthresh.m0000644000175000017500000001254615246255447017415 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.20.1/inst/PaxHeaders/lab2rgb.m0000644000000000000000000000006115246255447014421 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/lab2rgb.m0000644000175000017500000001160215246255447016510 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.20.1/inst/PaxHeaders/makelut.m0000644000000000000000000000006115246255447014550 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/makelut.m0000644000175000017500000000515715246255447016647 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.20.1/inst/PaxHeaders/tformfwd.m0000644000000000000000000000006115246255447014736 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/tformfwd.m0000644000175000017500000000432515246255447017031 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.20.1/inst/PaxHeaders/wavelength2rgb.m0000644000000000000000000000006115246255447016027 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/wavelength2rgb.m0000644000175000017500000001444115246255447020122 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.20.1/inst/PaxHeaders/histeq.m0000644000000000000000000000006115246255447014403 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/histeq.m0000644000175000017500000000673515246255447016505 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.20.1/inst/PaxHeaders/bestblk.m0000644000000000000000000000006115246255447014534 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/bestblk.m0000644000175000017500000000654215246255447016632 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.20.1/inst/PaxHeaders/rho_filter.m0000644000000000000000000000006115246255447015243 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/rho_filter.m0000644000175000017500000001250115246255447017331 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}) ## @deftypefnx {Function File} {[@var{filtered}, @var{filter}] =} rho_filter (@dots{}) ## ## Performs rho filtering on the parallel ray projections provided. ## ## The input parallel ray projections are the columns of @var{proj}. ## Filter 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. ## ## The frequency response of the filter in given in the vector @var{filter}. ## ## ## 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; ## Create the basic filter response n = new_len / 2 + 1; n = round(n); filt = zeros (n, 1); filt(1) = 0.25; for i=2:2:n filt(i) = -1 / (pi*(i-1))^2; end filt = [filt; filt(end-1:-1:2)]; rho = 2 * real (fft (filt)); rho(rho > scaling) = 0; ## Create the window to apply to the filter response f = (0:length (rho) / 2)' / length (rho); f = f / scaling; if (strcmpi (type, 'ram-lak')) filt = 1; elseif (strcmpi (type, 'hamming')) filt = 0.54 + 0.46 * cos (2 * pi * f); filt = [filt; filt(end - 1:-1:2)]; elseif (strcmpi (type, 'hann')) filt = 0.5 + 0.5 * cos (2 * pi * f); filt = [filt; filt(end - 1:-1:2)]; elseif (strcmpi (type, 'cosine')) filt = cos (pi * f); filt = [filt; filt(end - 1:-1:2)]; elseif (strcmpi (type, 'shepp-logan')) 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; 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.20.1/inst/PaxHeaders/imgaussfilt.m0000644000000000000000000000006115246255447015435 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imgaussfilt.m0000644000175000017500000004512315246255447017531 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 an image with a 2D gaussian kernel and returns a smoothed image. ## ## 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.20.1/inst/PaxHeaders/houghlines.m0000644000000000000000000000006115246255447015253 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/houghlines.m0000644000175000017500000003066115246255447017350 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.20.1/inst/PaxHeaders/label2rgb.m0000644000000000000000000000006115246255447014742 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/label2rgb.m0000644000175000017500000001431315246255447017033 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.20.1/inst/PaxHeaders/psf2otf.m0000644000000000000000000000006115246255447014471 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/psf2otf.m0000644000175000017500000000660615246255447016570 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.20.1/inst/PaxHeaders/mat2gray.m0000644000000000000000000000006115246255447014634 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/mat2gray.m0000644000175000017500000001147715246255447016735 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.20.1/inst/PaxHeaders/im2bw.m0000644000000000000000000000006115246255447014126 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/im2bw.m0000644000175000017500000001352515246255447016223 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, adaptthresh, imbinarize} ## @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.20.1/inst/PaxHeaders/checkerboard.m0000644000000000000000000000006115246255447015522 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/checkerboard.m0000644000175000017500000001537615246255447017625 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.20.1/inst/PaxHeaders/stdfilt.m0000644000000000000000000000006115246255447014557 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/stdfilt.m0000644000175000017500000001137515246255447016655 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 dom_sz = size (domain); dom_sz(end+1:ndims (I)) = 1; pad = floor (dom_sz / 2); I = padarray (I, pad, padding, varargin{:}); even = (mod (dom_sz, 2) == 0); 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.20.1/inst/PaxHeaders/impyramid.m0000644000000000000000000000006115246255447015101 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/impyramid.m0000644000175000017500000002151615246255447017175 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.20.1/inst/PaxHeaders/imsubtract.m0000644000000000000000000000006115246255447015263 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imsubtract.m0000644000175000017500000001023015246255447017346 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 two images or a 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.20.1/inst/PaxHeaders/bwareafilt.m0000644000000000000000000000006115246255447015226 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/bwareafilt.m0000644000175000017500000002451215246255447017321 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.20.1/inst/PaxHeaders/qtsetblk.m0000644000000000000000000000006115246255447014737 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/qtsetblk.m0000644000175000017500000000504715246255447017034 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.20.1/inst/PaxHeaders/imimposemin.m0000644000000000000000000000006115246255447015434 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imimposemin.m0000644000175000017500000001760315246255447017532 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 image to only have regional minima at the marker ## positions given by the nonzero pixels of given matrix. ## ## 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.20.1/inst/PaxHeaders/isbw.m0000644000000000000000000000006115246255447014052 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/isbw.m0000644000175000017500000000674715246255447016157 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 a given image 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.20.1/inst/PaxHeaders/integralImage3.m0000644000000000000000000000006115246255447015741 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/integralImage3.m0000644000175000017500000000753115246255447020036 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.20.1/inst/PaxHeaders/imshear.m0000644000000000000000000000006115246255447014536 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imshear.m0000644000175000017500000001341515246255447016631 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 a given image. ## ## 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.20.1/inst/PaxHeaders/hough.m0000644000000000000000000000006115246255447014220 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/hough.m0000644000175000017500000002126615246255447016316 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.20.1/inst/PaxHeaders/colfilt.m0000644000000000000000000000006115246255447014542 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/colfilt.m0000644000175000017500000001544415246255447016641 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.20.1/inst/PaxHeaders/montage.m0000644000000000000000000000006115246255447014540 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/montage.m0000644000175000017500000003441715246255447016640 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.20.1/inst/PaxHeaders/deconvwnr.m0000644000000000000000000000006115246255447015113 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/deconvwnr.m0000644000175000017500000004543715246255447017217 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.20.1/inst/PaxHeaders/imfilter.m0000644000000000000000000000006115246255447014721 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imfilter.m0000644000175000017500000002103115246255447017005 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{}) ## Apply a linear filter on a given image. ## ## 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.20.1/inst/PaxHeaders/houghpeaks.m0000644000000000000000000000006115246255447015244 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/houghpeaks.m0000644000175000017500000002606015246255447017337 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.20.1/inst/PaxHeaders/roicolor.m0000644000000000000000000000006115246255447014736 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/roicolor.m0000644000175000017500000000414315246255447017027 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.20.1/inst/PaxHeaders/imboxfilt.m0000644000000000000000000000006115246255447015103 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imboxfilt.m0000644000175000017500000004170115246255447017175 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 of an image, 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.20.1/inst/PaxHeaders/tforminv.m0000644000000000000000000000006115246255447014752 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/tforminv.m0000644000175000017500000000432715246255447017047 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.20.1/inst/PaxHeaders/im2single.m0000644000000000000000000000006115246255447014777 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/im2single.m0000644000175000017500000000552315246255447017073 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} {} 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.20.1/inst/PaxHeaders/rgb2ycbcr.m0000644000000000000000000000006115246255447014765 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/rgb2ycbcr.m0000644000175000017500000000541715246255447017063 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.20.1/inst/PaxHeaders/graythresh.m0000644000000000000000000000006115246255447015266 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/graythresh.m0000644000175000017500000010022015246255447017350 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, adaptthresh, imbinarize} ## @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.20.1/inst/PaxHeaders/ntsc2rgb.m0000644000000000000000000000006115246255447014632 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/ntsc2rgb.m0000644000175000017500000001440415246255447016724 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.20.1/inst/PaxHeaders/imhist.m0000644000000000000000000000006115246255447014403 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imhist.m0000644000175000017500000001624515246255447016502 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 an image. ## ## 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 call_colorbar () endif endfunction function call_colorbar() # inner function to call colorbar after changing its API in version 11.1.0 if verLessThan("Octave", "11.1") colorbar ("xticklabel", [], "SouthOutside"); else colorbar ("SouthOutside", "xticklabel", []); endif endfunction %!test %!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'}) %!test %! imhist(checkerboard()) %! close image-2.20.1/inst/PaxHeaders/imopen.m0000644000000000000000000000006115246255447014375 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imopen.m0000644000175000017500000001120115246255447016457 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.20.1/inst/PaxHeaders/imadd.m0000644000000000000000000000006115246255447014164 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imadd.m0000644000175000017500000000716015246255447016257 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 two images or a 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.20.1/inst/PaxHeaders/graycomatrix.m0000644000000000000000000000006115246255447015617 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/graycomatrix.m0000644000175000017500000004764715246255447017730 0ustar00avinoamavinoam00000000000000## Copyright (C) 2025 The Octave Project Developers ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 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{[glcm, scaled_image]} =} graycomatrix (@var{img}) ## @deftypefnx {Function File} {@var{[glcm, scaled_image]} =} graycomatrix (@var{img}, @var{Name}, @var{Value}, @dots{}) ## ## Compute the gray-level co-occurrence matrix (GLCM) for an image. ## ## @code{graycomatrix} calculates the GLCM for the input image @var{img}. The GLCM ## is a statistical method of examining the textures that considers the spatial ## relationship of pixels. ## ## @strong{Inputs} ## @table @var ## @item img ## A grayscale image (2D matrix) for which the GLCM is to be computed. ## Image type can be int8, uint8, int16, uint16, int32, uint32, single, double ## or logical. ## @end table ## ## @strong{Name-Value Pair Arguments} ## @table @var ## @item 'Offset' ## An M-by-2 array specifying the pixel pair offsets for which the GLCM is ## calculated. Each row in the array is a two-element integer vector [row_offset, col_offset]. ## The default is [0 1], which calculates the GLCM for horizontally adjacent ## pixels. ## ## @item 'NumLevels' ## A scalar specifying the number of gray levels to use when scaling the input ## image. The default is 2 for logical image and 8 for any other image. ## The image is scaled to have integer values between1 and the number of levels. ## ## @item 'GrayLimits' ## A two-element vector [low high] that specifies the intensity values in @var{img} ## that are to be considered when scaling the image to the number of levels. ## The default is the range of the class of @var{img}. if [] is used as input, ## the graylimits are [min(img(:)) max(img(:))]. ## ## @item 'Symmetric' ## A logical value indicating whether the GLCM should be symmetric. If true, ## the GLCM is averaged with its transpose to ensure symmetry. The default is ## false. ## ## @item 'use_oct' ## A logical value indicating whether the GLCM computation will use oct file. ## If true the GLCM is computed using cpp code and oct interface. ## If false, the computation is done using Octave function compute_glcm. ## The default is true. The oct computation is a little bit faster, ## and the compute_glcm is left here for educational reasons (Octave only parameter). ## ## @end table ## ## @strong{Outputs} ## @table @var ## @item glcm ## The gray-level co-occurrence matrix or matrices. If multiple offsets are ## specified, @var{glcm} will be a 3D array, where each slice along the third ## dimension corresponds to the GLCM for a particular offset. ## ## @item scaled_image ## Scaled image used to calculate the GLCM (type double). ## @end table ## ## @strong{Example} ## @example ## @group ## img = phantom(); ## glcm = graycomatrix (img, 'NumLevels', 16, 'Offset', [0 1; -1 1], 'Symmetric', true); ## @end group ## @end example ## ## @seealso{imhist} ## @end deftypefn function [glcm, scaled_image] = graycomatrix (img, varargin) if (nargin == 0) print_usage (); elseif (! isimage (img)) error ("graycomatrix: first argument must be an image") elseif (ndims (img) > 2) error ("graycomatrix: img should be a 2D image") endif if (any (isnan (img(:)))) warning ("Image:Graycomatrix-ignores-nan", "graycomatrix ignores NaN values"); endif # Parse inputs params = parse_graycomatrix_inputs (img, varargin{:}); # Compute scaled image scaled_image = compute_scaled_image (img, params); # Compute raw GLCM if (params.use_oct) glcm = __graycomatrix__ (scaled_image - 1, params.offset, ... params.num_levels, params.symmetric); else glcm = compute_glcm (scaled_image, params); endif endfunction function params = parse_graycomatrix_inputs (img, varargin) # parse_graycomatrix_inputs: Parse and validate input parameters for graycomatrix. # Parameters: # img: Input image. # varargin: Name-value pairs. # Returns: # params: Struct of parsed parameters. # parse input params p = inputParser; p.CaseSensitive = false; p.FunctionName = "graycomatrix"; if isa(img, 'logical') default_num_levels = 2; else default_num_levels = 8; endif # Optional name-value addParameter (p, "Offset", [0 1], @ (x) isnumeric (x) && size (x,2) == 2); addParameter (p, "NumLevels", default_num_levels, ... @ (x) isnumeric (x) && isscalar (x) && x >= 1); addParameter (p, "GrayLimits", double (getrangefromclass (img)), @ (x) isempty (x) || (isnumeric (x) && numel (x)==2)); addParameter (p, "Symmetric", false, @ (x) islogical (x)); addParameter (p, "use_oct", true, @ (x) islogical (x)); parse (p, varargin{:}); results = p.Results; # GrayLimits default if isempty (results.GrayLimits) gray_limits = double ([min(img(:)) max(img(:))]); else gray_limits = double (results.GrayLimits(:)'); end # Pack params params.offset = double (results.Offset); params.num_levels = double (results.NumLevels); params.gray_limits = gray_limits; params.symmetric = logical (results.Symmetric); params.use_oct = logical (results.use_oct); endfunction function scaled_image = compute_scaled_image (img, params) # compute_scaled_image: Scale image to integer levels for GLCM computation. # Parameters: # img: Input image. # params: Struct of parameters. # Returns: # scaled_image: Scaled image (type double). img = double (img); num_levels = params.num_levels; gray_limits = params.gray_limits; scaled_image = nan (size(img)); # Initialize with NaN valid_mask = ! isnan (img); # Clip and scale image to [1..numLevels] if (gray_limits (1) == gray_limits (2)) scaled_image(valid_mask) = 1; else scaled_image(valid_mask) = floor ((img(valid_mask) - gray_limits(1)) / ... (gray_limits(2) - gray_limits(1)) * num_levels + 1); scaled_image(scaled_image < 1 & valid_mask) = 1; scaled_image(scaled_image > num_levels & valid_mask) = num_levels; endif endfunction function glcm = compute_glcm (scaled_image, params) # compute_glcm: Compute gray-level co-occurrence matrix (GLCM) for scaled image. # Parameters: # scaled_image: Scaled image (type double). # params: Struct of parameters. # Returns: # glcm: GLCM matrix or matrices. # Mask out pairs with NaN in both ref and neighbor # Only valid pairs are counted in GLCM # compute glcm matrix num_levels = params.num_levels; offset = params.offset; symmetric = params.symmetric; n_offsets = size (offset, 1); glcm = zeros (num_levels, num_levels, n_offsets); [rows, cols] = size (scaled_image); num_levels2 = num_levels * num_levels; for k = 1:n_offsets dy = offset(k, 1); dx = offset(k, 2); # valid pixel ranges r1 = max (1, 1 - dy) : min (rows, rows - dy); c1 = max (1, 1 - dx) : min (cols, cols - dx); r2 = r1 + dy; c2 = c1 + dx; ref = scaled_image(r1, c1); neighbor = scaled_image(r2, c2); # Mask out pairs with NaN valid_mask = !isnan(ref) & !isnan(neighbor); ref_valid = ref(valid_mask); neighbor_valid = neighbor(valid_mask); lin_ind = sub2ind ([num_levels, num_levels], ref_valid(:), neighbor_valid(:)); # count occurrences counts = accumarray (lin_ind, 1, [num_levels2, 1]); gl = reshape (counts, num_levels, num_levels); if (symmetric) gl = gl + gl'; endif glcm(:, :, k) = gl; endfor endfunction %!test %! # Test basic functionality with default parameters %! img = [1 1 5 6; 3 5 7 1; 5 6 7 8; 8 1 5 1]; %! img = double(img) / 8; %! [glcm, scaled_image] = graycomatrix (img); %! expected_si = [2 2 6 7; 4 6 8 2; 6 7 8 8; 8 2 6 2]; %! expected_glcm = zeros (8, 8); %! expected_glcm(2, 2) = 1; expected_glcm(2, 6) = 2; expected_glcm(4,6) = 1; %! expected_glcm(6, 2) = 1; expected_glcm(6, 7) = 2; expected_glcm(6, 8) = 1; %! expected_glcm(7, 8) = 1; expected_glcm(8, 2) = 2; expected_glcm(8, 8) = 1; %! assert (isequal (scaled_image, expected_si)); %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with NumLevels parameter %! img = [1 2; 3 4]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 4); %! expected_si = 4*ones (2, 2); %! expected_glcm = zeros (4, 4); %! expected_glcm(4,4) = 2; %! assert (isequal (scaled_image, expected_si)); %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with GrayLimits parameter %! img = [0 0.2; 0.6 1.0]; %! [glcm, scaled_image] = graycomatrix (img, "GrayLimits", [0 1], "NumLevels", 4); %! expected_si = [1 1; 3 4]; %! expected_glcm = zeros (4, 4); %! expected_glcm(1,1) = 1; expected_glcm(3,4) = 1; %! assert (isequal (scaled_image, expected_si)); %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with Offset parameter - horizontal right [0 1] %! img = [1 2 3; 4 5 6; 7 8 9]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 9); %! expected_glcm = zeros (9, 9); %! expected_glcm(9,9) = 6; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with Offset parameter - vertical down [1 0] %! img = [1 2 3; 4 5 6; 7 8 9]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 9, "Offset", [1 0], "GrayLimits", []); %! expected_glcm = zeros (9, 9); %! expected_glcm(1,4) = 1; expected_glcm(2,5) = 1; expected_glcm(3,6) = 1; %! expected_glcm(4,7) = 1; expected_glcm(5,8) = 1; expected_glcm(6,9) = 1; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with Offset parameter - vertical down [1 0] %! img = [1 2 3; 4 5 6; 7 8 9]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 9, "Offset", [1 0]); %! expected_glcm = zeros (9, 9); %! expected_glcm(9,9) = 6; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with Offset parameter - vertical down [1 0] %! img = [1 2 3; 4 5 6; 7 8 9]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 9, "Offset", [1 0], "GrayLimits", []); %! expected_glcm = zeros (9, 9); %! expected_glcm(1,4) = 1; expected_glcm(2,5) = 1; expected_glcm(3,6) = 1; %! expected_glcm(4,7) = 1; expected_glcm(5,8) = 1; expected_glcm(6,9) = 1; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with multiple offsets %! img = [1 2; 3 4]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 4, "Offset", [0 1; 1 0], "GrayLimits", []); %! expected_glcm1 = zeros (4, 4); %! expected_glcm1(1, 2) = 1; expected_glcm1(3, 4) = 1; %! expected_glcm2 = zeros (4, 4); %! expected_glcm2(1, 3) = 1; expected_glcm2(2, 4) = 1; %! assert (isequal (glcm(:, :, 1), expected_glcm1)); %! assert (isequal (glcm(:, :, 2), expected_glcm2)); %!test %! # Test with Symmetric parameter %! img = [1 2; 3 4]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 4, "GrayLimits", [], "Symmetric", true); %! expected_glcm = zeros (4, 4); %! expected_glcm(1,2) = 1; expected_glcm(2,1) = 1; %! expected_glcm(3,4) = 1; expected_glcm(4,3) = 1; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with constant image %! img = ones (3, 3); %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 8); %! expected_glcm = zeros (8, 8); %! expected_glcm(8, 8) = 6; # 6 horizontal neighbor pairs all with value 1 %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with edge cases - check if offset goes outside image bounds %! img = [1 2; 3 4]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 4, "Offset", [2 2]); %! expected_glcm = zeros (4, 4); # Should be empty as all pairs are outside bounds %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with edge cases - equal GrayLimits %! img = [1 2; 3 4]; %! [glcm, scaled_image] = graycomatrix (img, "GrayLimits", [2 2]); %! expected_si = ones (2, 2); # All pixels should be mapped to 1 %! expected_glcm = zeros (8, 8); %! expected_glcm(1,1) = 2; # 2 horizontal neighbor pairs all with value 1 %! assert (isequal (scaled_image, expected_si)); %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with negative offsets %! img = [1 2; 3 4]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 4, "Offset", [-1 0], "GrayLimits", []); %! # This is equivalent to offset [1 0] but in the opposite direction %! expected_glcm = zeros (4, 4); %! expected_glcm(3,1) = 1; expected_glcm(4,2) = 1; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with diagonal offset %! img = [1 2 3; 4 5 6; 7 8 9]; %! [glcm, scaled_image] = graycomatrix (img, "NumLevels", 9, "Offset", [1 1], "GrayLimits", []); %! expected_glcm = zeros (9, 9); %! expected_glcm(1,5) = 1; expected_glcm(2,6) = 1; %! expected_glcm(4,8) = 1; expected_glcm(5,9) = 1; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with phantom input %! img = phantom(); %! glcm = graycomatrix (img, "NumLevels", 5); %! expected_glcm = [58979 105 0 0 457; %! 105 2788 0 0 0; %! 0 0 0 0 0; %! 0 0 0 0 0; %! 457 0 0 0 2389]; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test with phantom input %! img = phantom(); %! glcm = graycomatrix (img, "NumLevels", 5, "Symmetric", true); %! expected_glcm = [58979 105 0 0 457; %! 105 2788 0 0 0; %! 0 0 0 0 0; %! 0 0 0 0 0; %! 457 0 0 0 2389]; %! assert (isequal (glcm, expected_glcm + expected_glcm')); %!test %! # Test with phantom input %! img = phantom(); %! glcm = graycomatrix (img, "NumLevels", 5, "GrayLimits", []); %! expected_glcm = [37552 176 0 0 234; %! 176 24015 6 0 223; %! 0 6 46 0 0; %! 0 0 0 0 0; %! 234 223 0 0 2389]; %! assert (isequal (glcm, expected_glcm)); %!test %! # Test use_oct %! img = phantom(); %! glcm1 = graycomatrix (img, "use_oct", true); %! glcm2 = graycomatrix (img, "use_oct", false); %! assert (isequal (glcm1, glcm2)); %!test %! # Test use_oct %! img = phantom(); %! glcm1 = graycomatrix (img, "NumLevels", 5, "GrayLimits", []); %! glcm2 = graycomatrix (img, "NumLevels", 5, "GrayLimits", [], "use_oct", true); %! assert (isequal (glcm1, glcm2)); %!test %! # Test use_oct %! img = phantom(); %! glcm1 = graycomatrix (img, "NumLevels", 5, "GrayLimits", [], "use_oct", true); %! glcm2 = graycomatrix (img, "NumLevels", 5, "GrayLimits", [], "use_oct", false); %! assert (isequal (glcm1, glcm2)); %!test %! # Test with phantom input %! img = phantom(); %! glcm1 = graycomatrix (img, "use_oct", true); %! glcm2 = graycomatrix (img, "use_oct", false); %! assert (isequal (glcm1, glcm2)); %!test %! # test with different image types %! %! # uint8 %! expected_glcm = zeros (8); %! expected_glcm(1:4, 1:4) = [ 15, 7, 7, 1; ... %! 3, 16, 7, 0; ... %! 6, 9, 11, 3; ... %! 2, 0, 3, 0]; %! assert (isequal (graycomatrix (uint8(magic(10))), expected_glcm)); %! %! # uint16 %! expected_glcm = zeros (8); %! expected_glcm(1, 1) = 90; %! assert (isequal (graycomatrix (uint16(magic(10))), expected_glcm)); %! %! # uint32 %! assert (isequal (graycomatrix (uint32(magic(10))), expected_glcm)); %! %! # int8 %! expected_glcm = zeros (8); %! expected_glcm(5:8, 5:8) = [ 15, 7, 7, 1; ... %! 3, 16, 7, 0; ... %! 6, 9, 11, 3; ... %! 2, 0, 3, 0]; %! assert (isequal (graycomatrix (int8(magic(10))), expected_glcm)); %! %! # int16 %! expected_glcm = zeros (8); %! expected_glcm(5, 5) = 90; %! assert (isequal (graycomatrix (int16(magic(10))), expected_glcm)); %! %! # int32 %! assert (isequal (graycomatrix (int32(magic(10))), expected_glcm)); %! %! # single %! expected_glcm = zeros (8); %! expected_glcm(8, 8) = 90; %! assert (isequal (graycomatrix (single(magic(10))), expected_glcm)); % %! # double %! assert (isequal (graycomatrix (double(magic(10))), expected_glcm)); %! # logical %! assert (isequal (graycomatrix (magic(10) > 50), [29, 16; 16, 29])); %!test %! # bug #67905 %! %! # Save current warning state %! state = warning; %! warning ("off", "Image:Graycomatrix-ignores-nan"); %! img = [1:5; 6:10]; img(end) = nan; %! [glcm, SI] = graycomatrix (img); %! glcm_exp = zeros (8); glcm_exp(8, 8) = 7; %! SI_exp = 8 * ones (2, 5); SI_exp(2, 5) = NaN; %! assert (isequal (glcm, glcm_exp)) %! assert (isequaln (SI, SI_exp)) %! [glcm, SI] = graycomatrix (img, 'use_oct', true); %! assert (isequal (glcm, glcm_exp)) %! assert (isequaln (SI, SI_exp)) %! [glcm, SI] = graycomatrix (img, 'use_oct', false); %! assert (isequal (glcm, glcm_exp)) %! assert (isequaln (SI, SI_exp)) %! # Restore the original warning state %! warning (state); %!error graycomatrix () %!error graycomatrix ("xxx") %!error graycomatrix ([]) %!error graycomatrix (1, 2) %!error graycomatrix (cat(3, [1 2], [1 2], [1 2])) %!error graycomatrix ([1, 2], "AAA") %!error graycomatrix (phantom(), "GrayLimits", 0) %!error graycomatrix (phantom(), "GrayLimits", "aa") %!error graycomatrix (phantom(), "NumLevels", "0") %!error graycomatrix (phantom(), "Symmetric", 1); %!error graycomatrix (phantom(), "Offset", 1); %!error graycomatrix (phantom(), "Offset", [1, 2]'); %!error graycomatrix (phantom(), "Offset", true); %!error graycomatrix (phantom(), "Offset", [[1 2 3 4]]); %!demo %! % Demonstration of graycomatrix with phantom image %! disp("Demonstrating graycomatrix with the Shepp-Logan phantom:"); %! P = phantom(50); %! figure(1); imagesc(P); colormap(gray); title("Phantom Image"); %! colorbar; axis image; %! %! % Calculate GLCMs with different parameters %! [glcm1, SI] = graycomatrix (P, "NumLevels", 8, "Offset", [0 1]); %! [glcm2, ~] = graycomatrix (P, "NumLevels", 8, "Offset", [1 0]); %! [glcm3, ~] = graycomatrix (P, "NumLevels", 8, "Offset", [1 1]); %! [glcm4, ~] = graycomatrix (P, "NumLevels", 8, "Offset", [0 1], "Symmetric", true); %! %! % Display the scaled image and GLCMs %! figure(2); imagesc(SI); colormap(gray); title("Scaled Image (8 levels)"); %! colorbar; axis image; %! %! figure(3); %! subplot(2,2,1); imagesc(log2(glcm1+1)); title("log2 GLCM [0 1]"); colorbar; %! subplot(2,2,2); imagesc(log2(glcm2+1)); title("log2 GLCM [1 0]"); colorbar; %! subplot(2,2,3); imagesc(log2(glcm3+1)); title("log2 GLCM [1 1]"); colorbar; %! subplot(2,2,4); imagesc(log2(glcm4+1)); title("log2 GLCM [0 1] Symmetric"); colorbar; %! %! % Calculate and display GLCMs with different number of levels %! figure(4); %! levels = [4, 8, 16, 32]; %! for i = 1:length(levels) %! [glcm, ~] = graycomatrix (P, "NumLevels", levels(i)); %! subplot(2,2,i); %! imagesc(glcm); %! title(sprintf("GLCM with %d levels", levels(i))); %! colorbar; %! end %! %! disp("The figures show how different parameters affect the GLCM computation."); %! disp("Figure 1: Original phantom image"); %! disp("Figure 2: Scaled image with 8 gray levels"); %! disp("Figure 3: GLCMs with different offsets and symmetry"); %! disp("Figure 4: GLCMs with different numbers of gray levels"); image-2.20.1/inst/PaxHeaders/imcast.m0000644000000000000000000000006115246255447014366 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imcast.m0000644000175000017500000001616615246255447016467 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. ## ## 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.20.1/inst/PaxHeaders/applylut.m0000644000000000000000000000006115246255447014760 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/applylut.m0000644000175000017500000000417015246255447017051 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.20.1/inst/PaxHeaders/imextendedmin.m0000644000000000000000000000006115246255447015740 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imextendedmin.m0000644000175000017500000001354715246255447020041 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 a given image. ## ## 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.20.1/inst/PaxHeaders/private0000644000000000000000000000013015246255465014322 xustar0030 mtime=1788435253.497016428 29 atime=1788435255.25206223 29 ctime=1788435255.25206223 image-2.20.1/inst/private/0000755000175000017500000000000015246255465016471 5ustar00avinoamavinoam00000000000000image-2.20.1/inst/private/PaxHeaders/imarithmetics.m0000644000000000000000000000006115246255447017422 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/imarithmetics.m0000644000175000017500000000716715246255447021524 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.20.1/inst/private/PaxHeaders/colorspace_conversion_input_check.m0000644000000000000000000000006115246255447023533 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/colorspace_conversion_input_check.m0000644000175000017500000000611415246255447025624 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.20.1/inst/private/PaxHeaders/ispart.m0000644000000000000000000000006115246255447016062 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/ispart.m0000644000175000017500000000211615246255447020151 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.20.1/inst/private/PaxHeaders/analyze75filename.m0000644000000000000000000000006115246255447020100 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/analyze75filename.m0000644000175000017500000000325015246255447022167 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.20.1/inst/private/PaxHeaders/colorspace_conversion_revert.m0000644000000000000000000000006115246255447022546 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/colorspace_conversion_revert.m0000644000175000017500000000275115246255447024642 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.20.1/inst/private/PaxHeaders/pad_for_sliding_filter.m0000644000000000000000000000006115246255447021250 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/pad_for_sliding_filter.m0000644000175000017500000000316315246255447023342 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.20.1/inst/private/PaxHeaders/is_float_image.m0000644000000000000000000000006115246255447017522 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/is_float_image.m0000644000175000017500000000164415246255447021616 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.20.1/inst/private/PaxHeaders/handle_colorspec.m0000644000000000000000000000006115246255447020064 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/handle_colorspec.m0000644000175000017500000000274415246255447022162 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.20.1/inst/private/PaxHeaders/ycbcrfunc.m0000644000000000000000000000006115246255447016536 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/ycbcrfunc.m0000644000175000017500000000766715246255447020645 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.20.1/inst/private/PaxHeaders/isimage.m0000644000000000000000000000006115246255447016176 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/isimage.m0000644000175000017500000000175715246255447020277 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.20.1/inst/private/PaxHeaders/istform.m0000644000000000000000000000006115246255447016243 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/istform.m0000644000175000017500000000241115246255447020330 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.20.1/inst/private/PaxHeaders/im2col_check.m0000644000000000000000000000006115246255447017102 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/im2col_check.m0000644000175000017500000000373115246255447021175 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.20.1/inst/private/PaxHeaders/lab2cls.m0000644000000000000000000000006115246255447016102 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/lab2cls.m0000644000175000017500000000635015246255447020175 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.20.1/inst/private/PaxHeaders/interp_method.m0000644000000000000000000000006115246255447017421 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/interp_method.m0000644000175000017500000000262115246255447021511 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.20.1/inst/private/PaxHeaders/prepare_strel.m0000644000000000000000000000006115246255447017427 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/private/prepare_strel.m0000644000175000017500000000272315246255447021522 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.20.1/inst/PaxHeaders/imsharpen.m0000644000000000000000000000006115246255447015074 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imsharpen.m0000644000175000017500000002120515246255447017163 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.20.1/inst/PaxHeaders/immaximas.m0000644000000000000000000000006115246255447015073 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/immaximas.m0000644000175000017500000001114315246255447017162 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.20.1/inst/PaxHeaders/std2.m0000644000000000000000000000006115246255447013762 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/std2.m0000644000175000017500000000522215246255447016052 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.20.1/inst/PaxHeaders/lab2double.m0000644000000000000000000000006115246255447015121 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/lab2double.m0000644000175000017500000000773015246255447017217 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.20.1/inst/PaxHeaders/bwselect.m0000644000000000000000000000006115246255447014716 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/bwselect.m0000644000175000017500000000654215246255447017014 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.20.1/inst/PaxHeaders/imhmax.m0000644000000000000000000000006115246255447014371 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imhmax.m0000644000175000017500000001360615246255447016466 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.20.1/inst/PaxHeaders/imextendedmax.m0000644000000000000000000000006115246255447015742 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imextendedmax.m0000644000175000017500000001353615246255447020041 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 a given image. ## ## 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.20.1/inst/PaxHeaders/padarray.m0000644000000000000000000000006115246255447014711 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/padarray.m0000644000175000017500000006127215246255447017010 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.20.1/inst/PaxHeaders/lab2xyz.m0000644000000000000000000000006115246255447014501 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/lab2xyz.m0000644000175000017500000001247015246255447016574 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.20.1/inst/PaxHeaders/impixel.m0000644000000000000000000000006115246255447014555 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/impixel.m0000644000175000017500000001501015246255447016641 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.20.1/inst/PaxHeaders/@imref3d0000644000000000000000000000013015246255465014301 xustar0030 mtime=1788435253.443015018 29 atime=1788435255.25206223 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/0000755000175000017500000000000015246255465016450 5ustar00avinoamavinoam00000000000000image-2.20.1/inst/@imref3d/PaxHeaders/contains.m0000644000000000000000000000006115246255447016355 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/contains.m0000644000175000017500000000546515246255447020456 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.20.1/inst/@imref3d/PaxHeaders/disp.m0000644000000000000000000000006115246255447015476 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/disp.m0000644000175000017500000000333215246255447017566 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}) ## Display the properties of imref3d object ## ## @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.20.1/inst/@imref3d/PaxHeaders/intrinsicToWorld.m0000644000000000000000000000006115246255447020054 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/intrinsicToWorld.m0000644000175000017500000000760315246255447022151 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.20.1/inst/@imref3d/PaxHeaders/worldToIntrinsic.m0000644000000000000000000000006115246255447020054 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/worldToIntrinsic.m0000644000175000017500000000664515246255447022156 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.20.1/inst/@imref3d/PaxHeaders/subsref.m0000644000000000000000000000006115246255447016210 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/subsref.m0000644000175000017500000000430715246255447020303 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}) ## Implementation of subref method to imref3d object ## ## @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.20.1/inst/@imref3d/PaxHeaders/imref3d.m0000644000000000000000000000006115246255447016070 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/imref3d.m0000644000175000017500000003216115246255447020162 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 3D 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.20.1/inst/@imref3d/PaxHeaders/worldToSubscript.m0000644000000000000000000000006115246255447020070 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/worldToSubscript.m0000644000175000017500000000661315246255447022165 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.20.1/inst/@imref3d/PaxHeaders/subsasgn.m0000644000000000000000000000006115246255447016364 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/@imref3d/subsasgn.m0000644000175000017500000000705315246255447020460 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}) ## Implementation of subsasgn method to imref3d object ## ## @end deftypefn function rout = subsasgn (r, index, val) switch (index.type) case "." fld = index.subs; switch (fld) case "ImageSize" imageSize = val; if (length (imageSize) < 3) error ("Octave:invalid-input-arg", ... "ImageSize must have three elements"); endif validateattributes (imageSize, {"numeric"}, ... {"positive", "integer", "vector"}, "imref3d", "imageSize"); m = imageSize(1); n = imageSize(2); p = imageSize(3); rout = r; rout.ImageSize = imageSize; rout.PixelExtentInWorldX = r.ImageExtentInWorldX / n; rout.PixelExtentInWorldY = r.ImageExtentInWorldY / m; rout.PixelExtentInWorldZ = r.ImageExtentInWorldZ / p; rout.XIntrinsicLimits = [0.5, n + 0.5]; rout.YIntrinsicLimits = [0.5, m + 0.5]; rout.ZIntrinsicLimits = [0.5, p + 0.5]; case "XWorldLimits" xWorldLimits = val; validateattributes (xWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref3d", "xWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1); rout = r; rout.XWorldLimits = val; rout.ImageExtentInWorldX = imageExtentInWorldX; rout.PixelExtentInWorldX = imageExtentInWorldX / imageSize(2); case "YWorldLimits" yWorldLimits = val; validateattributes (yWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref3d", "yWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1); rout = r; rout.YWorldLimits = val; rout.ImageExtentInWorldY = imageExtentInWorldY; rout.PixelExtentInWorldY = imageExtentInWorldY / imageSize(1); case "ZWorldLimits" zWorldLimits = val; validateattributes (zWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref3d", "zWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldZ = zWorldLimits(2) - zWorldLimits(1); rout = r; rout.ZWorldLimits = val; rout.ImageExtentInWorldZ = imageExtentInWorldZ; rout.PixelExtentInWorldZ = imageExtentInWorldZ / imageSize(3); otherwise error ("Octave:invalid-indexing", ... "@imref3d/subsasgn: invalid property '%s'", fld); endswitch otherwise error ("Octave:invalid-indexing", "@imref3d/subsasgn: invalid index type") endswitch endfunction image-2.20.1/inst/PaxHeaders/imattributes.m0000644000000000000000000000006115246255447015622 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imattributes.m0000644000175000017500000001214715246255447017716 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.20.1/inst/PaxHeaders/fftconvn.m0000644000000000000000000000006115246255447014731 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/fftconvn.m0000644000175000017500000001374315246255447017030 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.20.1/inst/PaxHeaders/entropyfilt.m0000644000000000000000000000006115246255447015465 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/entropyfilt.m0000644000175000017500000001367215246255447017565 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 dom_sz = size (domain); dom_sz(end+1:ndims (I)) = 1; pad = floor (dom_sz / 2); I = padarray (I, pad, padding, varargin{:}); even = (mod (dom_sz, 2) == 0); 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.20.1/inst/PaxHeaders/imabsdiff.m0000644000000000000000000000006115246255447015032 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imabsdiff.m0000644000175000017500000000606115246255447017124 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 two images or a 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.20.1/inst/PaxHeaders/im2uint16.m0000644000000000000000000000006115246255447014644 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/im2uint16.m0000644000175000017500000000624015246255447016735 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.20.1/inst/PaxHeaders/analyze75info.m0000644000000000000000000000006115246255447015601 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/analyze75info.m0000644000175000017500000001441515246255447017675 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.20.1/inst/PaxHeaders/imhmin.m0000644000000000000000000000006115246255447014367 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/imhmin.m0000644000175000017500000001444615246255447016467 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.20.1/inst/PaxHeaders/bwperim.m0000644000000000000000000000006115246255447014553 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/bwperim.m0000644000175000017500000002300015246255447016635 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.20.1/inst/PaxHeaders/mean2.m0000644000000000000000000000006115246255447014110 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/mean2.m0000644000175000017500000000333715246255447016205 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.20.1/inst/PaxHeaders/colorangle.m0000644000000000000000000000006115246255447015233 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/colorangle.m0000644000175000017500000001333215246255447017324 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.20.1/inst/PaxHeaders/getrangefromclass.m0000644000000000000000000000006115246255447016614 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/getrangefromclass.m0000644000175000017500000000530515246255447020706 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.20.1/inst/PaxHeaders/bwmorph.m0000644000000000000000000000006115246255447014564 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/bwmorph.m0000644000175000017500000013143715246255447016664 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.20.1/inst/PaxHeaders/normxcorr2.m0000644000000000000000000000006115246255447015221 xustar0020 atime=1788435239 29 ctime=1788435255.25206223 image-2.20.1/inst/normxcorr2.m0000644000175000017500000002775515246255447017330 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Benjamin Eltzner ## Copyright (C) 2026 The Octave Project Developers ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 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{result} =} normxcorr2 (@var{template}, @var{img}) ## @deftypefnx {} {@var{result} =} normxcorr2 (@var{template}, @var{img}, @var{use_fft}) ## Compute the normalized cross-correlation of @var{template} and @var{img}. ## ## Both @var{template} and @var{img} must be real, grayscale 2-D images ## (row and column vectors are accepted as 2-D), and @var{template} must ## not be larger than @var{img} in any dimension. ## The template mean is subtracted before correlation, so a constant ## (zero-variance) template is rejected. ## ## The result is the same size as a @qcode{"full"} 2-D convolution, that is ## @code{size (@var{img}) + size (@var{template}) - 1}. Each value is the ## correlation coefficient for the corresponding alignment, normalized to ## the range @math{[-1, 1]}; locations where the local window is (near) flat ## are set to 0. Values in the border region, where @var{template} only ## partially overlaps @var{img}, are defined but not physically meaningful. ## ## The optional logical argument @var{use_fft} selects the implementation. ## When true, the correlation and local sums are computed via the FFT; when ## false, they are computed in the spatial domain with @code{conv2}. Both ## paths produce equivalent results. If omitted, an FFT is used when the ## template has more than 500 elements. ## ## @seealso{conv2, xcorr2, fft2} ## @end deftypefn function result = normxcorr2 (template, img, use_fft) if (nargin < 2) print_usage (); endif if (! isimage (template)) error ("normxcorr2: first argument [template] must be an image"); endif if (! isimage (img)) error ("normxcorr2: second argument [img] must be an image"); endif if (ndims (template) != 2 || ndims (img) != 2) error ("normxcorr2: TEMPLATE and IMG must be 1-D or 2-D (grayscale) images."); endif if (nargin == 3 && (! islogical (use_fft) || ! isscalar (use_fft))) error ("normxcorr2: third argument [use_fft] must be a scalar logical"); endif ## If this happens, it is probably a mistake. if (any (size (template) > size (img))) error ("normxcorr2: TEMPLATE larger than IMG. Arguments may be swapped."); endif if (nargin == 2) ## Heuristic from benchmarking: the FFT and spatial paths cross over ## around a template area of ~500 elements, roughly independent of image size. ## Below that spatial conv2 is faster; above it the FFT wins ## (increasingly so for large templates). use_fft = (numel (template) > 500); endif if (use_fft) result = normxcorr2_fft (template, img); else result = normxcorr2_spatial (template, img); endif endfunction function [template, img, template_sq_sum] = prepare_inputs (template, img) # Cast to double, subtract the template mean, and reject a constant template. template = double (template); img = double (img); template = template - mean (template(:)); template_sq_sum = sum (template(:) .^ 2); if (template_sq_sum == 0) error ("normxcorr2: Template has zero variance."); endif endfunction function result = normxcorr2_fft (template, img) [template, img, template_sq_sum] = prepare_inputs (template, img); # Sizes [t_rows, t_cols] = size (template); [i_rows, i_cols] = size (img); out_rows = i_rows + t_rows - 1; out_cols = i_cols + t_cols - 1; # Transform at the next fast (2/3/5/7-smooth) size >= the linear-conv size. # Zero-padding beyond out_rows/out_cols cannot alias, so cropping the # results back to out_rows x out_cols is exact, and FFTW avoids slow # large-prime transform lengths. fft_rows = fast_fft_size (out_rows); fft_cols = fast_fft_size (out_cols); # FFTs (fft_image is reused for both correlation and the local sum) fft_image = fft2 (img, fft_rows, fft_cols); fft_template = fft2 (rot90 (template, 2), fft_rows, fft_cols); cross_corr = real (ifft2 (fft_template .* fft_image)); # Local sums via a box kernel in the frequency domain fft_kernel = fft2 (ones (t_rows, t_cols), fft_rows, fft_cols); local_sum = real (ifft2 (fft_kernel .* fft_image)); fft_image_sq = fft2 (img .^ 2, fft_rows, fft_cols); local_sum_sq = real (ifft2 (fft_kernel .* fft_image_sq)); num_elements = t_rows * t_cols; local_std = max (local_sum_sq - local_sum .^ 2 / num_elements, 0); denom = sqrt (local_std * template_sq_sum); result = cross_corr ./ denom; result(isinf (result) | isnan (result)) = 0; # Suppress spurious peaks where the local window is (near) flat. result(denom < eps * max (denom(:))) = 0; # True NCC lies in [-1, 1]; clamp away floating-point overshoot. result = max (min (result, 1), -1); # Crop away the extra fast-size padding to the true "full" output size. result = result(1:out_rows, 1:out_cols); endfunction function m = fast_fft_size (n) # Smallest integer >= N whose only prime factors are 2, 3, 5 or 7. m = n; while (true) f = m; for p = [2 3 5 7] while (mod (f, p) == 0) f = f / p; endwhile endfor if (f == 1) break; endif m = m + 1; endwhile endfunction function result = normxcorr2_spatial (template, img) [template, img, template_sq_sum] = prepare_inputs (template, img); # Sizes [t_rows, t_cols] = size (template); cross_corr = conv2 (img, rot90(template, 2), "full"); # Compute local sums and stds. The box kernel ones(t_rows,t_cols) is # separable, so conv2(col_vec, row_vec, A) gives the same "full" result # as conv2(A, ones(...)) but in O(N*(t_rows+t_cols)) instead of # O(N*t_rows*t_cols). col_ones = ones (t_rows, 1); row_ones = ones (1, t_cols); local_sum = conv2 (col_ones, row_ones, img, "full"); local_sum_sq = conv2 (col_ones, row_ones, img .^ 2, "full"); num_elements = t_rows * t_cols; local_std = max (local_sum_sq - local_sum .^ 2 / num_elements, 0); denom = sqrt(local_std * template_sq_sum); result = cross_corr ./ denom; result(isinf (result) | isnan (result)) = 0; # Suppress spurious peaks where the local window is (near) flat. result(denom < eps * max (denom(:))) = 0; # True NCC lies in [-1, 1]; clamp away floating-point overshoot. result = max (min (result, 1), -1); 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)); %! a_shift2 = a - scale * ones (size (a)); %! assert (max ((normxcorr2 (a_shift1,b) - normxcorr2 (a,b))(:)), 0, 1e-10); %! assert (max ((normxcorr2 (a_shift2,b) - normxcorr2 (a,b))(:)), 0, 1e-10); ## 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) %!test %! template = reshape (1:9, [3, 3]); %! img = reshape (1:64, [8, 8]); %! out = normxcorr2 (template, img, false); %! assert (size (out), [10, 10]); %!test %! rand ("seed", 1); %! template = rand (7, 5); %! img = rand (20, 21); %! out_spatial = normxcorr2 (template, img, false); %! out_fft = normxcorr2 (template, img, true); %! assert (out_spatial, out_fft, 1e-9); %!test %! template = rand (4, 4); %! img = rand (12, 12); %! out_auto = normxcorr2 (template, img); %! out_spatial = normxcorr2 (template, img, false); %! assert (out_auto, out_spatial, 1e-12); ## Result stays within the valid NCC range [-1, 1]. %!test %! rand ("seed", 42); %! template = rand (5, 5); %! img = rand (15, 15); %! out = normxcorr2 (template, img, true); %! assert (all (out(:) >= -1 & out(:) <= 1)); ## FFT fast-size padding must stay exact when the "full" output size is ## prime (58+4-1 = 61, 63+5-1 = 67, both prime), so the transform length is ## padded up to a smooth size and cropped back. %!test %! rand ("seed", 7); %! template = rand (4, 5); %! img = rand (58, 63); %! out_fft = normxcorr2 (template, img, true); %! out_spatial = normxcorr2 (template, img, false); %! assert (size (out_fft), [61, 67]); %! assert (out_fft, out_spatial, 1e-9); ## test for bug #50151 %!test %! img = [0 1 2 1 2; %! 1 0 1 1 0; %! 0 1 2 2 0; %! 0 2 0 0 0; %! 2 1 0 0 0]; %! tmpl = [0 1 1]; %! corr = normxcorr2(tmpl, img); %! res = [ %! 0 0.5000 0.8660254 0.5000 -0.5000 0 -1.0000; %! 0.5000 0.5000 -0.5000 1.0000 -0.5000 -1.0000 0; %! 0 0.5000 0.8660254 1.0000 -0.5000 -1.0000 0; %! 0 0.5000 0.5000 -1.0000 0 0 0; %! 0.5000 0.8660254 -0.8660254 -1.0000 0 0 0]; %! assert(corr, res, 5e-9) %!error normxcorr2 (); %!error normxcorr2 (rand (5)); %!error normxcorr2 ({1}, rand (10, 10)); %!error normxcorr2 (rand (3, 3), {1}); %!error normxcorr2 (rand (5), rand (20), 2); %!error normxcorr2 (rand (10, 10), rand (5, 5)); %!error normxcorr2 (rand (3, 3), rand (10, 10), 1); %!error normxcorr2 (rand (3, 3), rand (10, 10), [true, false]); %!error