%kckmeans2 'Performs K-Means clustering with more options '
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros ckmeans2.pane file
%
% Parameters: 
% InputFile: i1 'Input data object', required: 'input data object (to be clustered)'
% InputFile: i2 'Cluster center input object', optional: 'cluster center input object'
% Toggle: map 'Generate output map', default: 0: 'generate output map'
% Toggle: spectrum 'SPECTRUM compatable map segment', default: 0: 'SPECTRUM compatable map segment'
% Integer: n 'Max number of iterations', default: 50000: 'max number of iterations'
% Integer: mergeifcount 'Cluster count is <', default: 1: 'merge with closest if the cluster has less than mergeifcount points'
% Integer: maskifcount 'Mask points in clusters with counts < ', default: 1: 'mask if point is in cluster with less than maskifcount'
% OutputFile: o1 'Cluster number output object', required: 'cluster number output object'
% OutputFile: o2 'Cluster center output object', optional: 'cluster center output object'
% OutputFile: o3 'Cluster variance output object', optional: 'cluster variance output object'
% OutputFile: o4 'Cluster membership count output', optional: 'Cluster membership count output'
% OutputFile: o5 'K-means statistics output (ASCII)', optional: 'K-means statistics output (ASCII)'
% Integer: k 'Initial number of clusters', default: 2: 'initial number of clusters'
%
% Example: [o1, o2, o3, o4, o5] = kckmeans2({i1, i2}, {'i1','';'i2','';'map',0;'spectrum',0;'n',50000;'mergeifcount',1;'maskifcount',1;'o1','';'o2','';'o3','';'o4','';'o5','';'k',2})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% ckmeans2 - Performs K-Means clustering with more options
%
%  DESCRIPTION
% "ckmeans2" is the implementation of a clustering algorithm that can be used for unsupervised classification of data. \fIckmeans2\fP has some enhancements over the \fIkkmeans\fP algorithm of the datamanip toolbox. These enhancements provides more variation of parameters for clustering but does not guarantee that better results could be obtained. 
% Main differences from "kkmeans" to \fIckmeans2\fP are:
% 1 - "ckmeans2" consider mask data. Masked points will not be taken in account for clustering and/or calculation of means and variances.
% 2 - If the user don't provide an initial cluster center object with the [-i2] parameter, "ckmeans2" will create the initial cluster centers from random sparse values - there is no guarantee that this will improve the convergence (in some cases may even slow down the clustering) but if you use the first N points from the data there is the risk that these points will be similar (e.g. with image data)
% 3 - "ckmeans2" allows merging of clusters based on the number of points in the clusters - this allow clusters with few points  (specified with the parameter [-mergeifcount]) to be joined to the nearest cluster. Clusters can also be joined if their distances is smaller than a threshold ([-mergeifdistance]). Cluster distances are calculated with the Euclidean distance of their centers. Please note that the joining algorithm is not optimum - joining is perfomed after kmeans clustering to avoid conflicts while clustering (slowing convergence), and is not done recursively until a stable state is achieved.
% 4 - "ckmeans2" allows the user to mask points belonging to clusters with few points (specified with the parameter [-maskifcount]). This has the effect of rejecting clusters with few points that can be considered as outliers on the data. A caveat is needed: the kmeans algorithm tries to create similar-sized, spherical-shaped clusters, but few points in a cluster does not necessarily means that this cluster is made of outliers.
% This implementation of "ckmeans2" uses the same basic clustering procedure as the \fIkkmeans\fP implementation in Khoros - meaning that the code was copied partially, specially for creation of that complicated SPECTRUM compatibility thingie. Please refer to the \fIkkmeans\fP help for more information about \fIkkmeans\fP.
% The input file is specified with the parameter [-i1], and optionally initial cluster centers can be passed with the [-i2] parameter. If passed, this data must have dimensions Cx1x1x1xE, where C is the number of classes and E is the number of elements in the [-i1] input data. 
% The number of clusters can be specified either with the [-k] parameter or by proportion [-divk] of the number of valid points on the data. The maximum number of iterations is specified with the [-n] parameter.
% The output file [-o1] can have a map segment that will be the center of the cluster for each cluster, if the parameter [-map] is passed. Optionally a Spectrum-compatable map can be created by specifying [-spectrum]. 
% As with "kkmeans", this routine can create several output files: [-o1] will contain the cluster numbers for each input vector, [-o2] will contain the cluster centers, [-o3] will contain the cluster variances, [-o4] will contain the cluster counts and [-o5]
% will contain information about the clustering (in ASCII). Please refer to the kman page for "kkmeans" for more details about these files.
% This routine is a first step towards a more complete implementation of the Isodata algorithm, which uses the k-means clustering algorithm for clustering and heuristics for post-clustering merging and splitting of clusters.
%
%  
%
%  EXAMPLES
% All examples for the Classify toolbox are listed on the Classify Toolbox Manual. For an example of this kroutine, please see the example workspace Classify:workspaces:kmeans
%
%  "SEE ALSO"
% kkmeans (in the Datamanip toolbox), cfuzzycmeans
%
%  RESTRICTIONS 
% Complex data types are not supported. If there is mask, expect the mask to be consistent over vectors, i.e. for a vector's elements either all masks are TRUE or FALSE. 
% At this implementation the final result will contain the same number of clusters as specified by [-k] or [-divk], even if joining is performed. Future versions should delete the clusters with zero membership.
%
%  REFERENCES 
% All references for the Classify toolbox are listed on the Classify Toolbox Manual.
%
%  COPYRIGHT
% Copyright (C) 1997 Rafael Santos. Khoros (C) Khoral Research, Inc.
% 


function varargout = kckmeans2(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kckmeans2(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i1', '__input';'i2', '__input';'map', 0;'spectrum', 0;'n', 50000;'mergeifcount', 1;'maskifcount', 1;'o1', '__output';'o2', '__output';'o3', '__output';'o4', '__output';'o5', '__output';'k', 2};
maxval={0,1,0,0,100000,2,2,0,1,1,1,1,2};
minval={0,1,0,0,0,2,2,0,1,1,1,1,2};
istoggle=[0,1,1,1,1,1,1,0,1,1,1,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','InputFile','Toggle','Toggle','Integer','Integer','Integer','OutputFile','OutputFile','OutputFile','OutputFile','OutputFile','Integer'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs)) 
        error(['Input ' narglist{jj,1} ' has no corresponding input!']); 
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout) 
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']); 
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)  
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in') 
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = ''; 
        end;
        narglist{jj,2} = ''; 
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = ''; 
          narglist{jj,2} = ''; 
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs)) 
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']); 
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs)) 
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else 
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else 
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1) 
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']); 
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs) 
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else 
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'ckmeans2"  '],Inputs,narglist);
