function [RESULT_CLA,iter] = SVMAL_ObjCan_Diversity(hyperdata,panseg,Groundtruth,sampleindex,ppara)
% [RESULT_CLA,iter] = SVMAL_ObjCan(hyperdata,pandata,Groundtruth,sampleindex,ppara)
% 主动学习辅助支持向量机分类
%Input:
%   hyperdata:          输入特征
%   panseg :            全色图像分割结果
%   DataTruth:          真值图
%   sampleindex:
%       {trainindex,validindex,testindex} 训练，验证，测试样本索引
%   ppara:
%       { a,b:          图像大小
%         select_per:   每次从候选样本集中加入的标记样本数
%         Maxiter:      最大迭代次数
%         AL_method:    从候选样本集中加入标记样本的方法
%         alpha:        置信度阈值（0～1）
%         delat :       光谱相似性阈值（剔除样本的个数）
%       }
%Ouput:
%   RESULT_CLA{1:iter}: struct cells
%       { ConfusionMatrix, OA, Kappa, producercc, usercc;}
%   RESULT_CLA{iter+1}: 最终的分类图
%   iter:   实际迭代次数
if ~isfield(ppara,'Maxiter'),   ppara.Maxiter = 20; end
if ~isfield(ppara,'delta'),     ppara.delta = 0;    end
if ~isfield(ppara,'DiversityRule'), ppara.DiversityRule = 'Norm'; end
if ~isfield(ppara,'StopCond'),      ppara.StopCond = 0;end
if ~isfield(ppara,'Mapping'),   ppara.Mapping = 0;  end
%% 训练样本所在对象内部所有像素的索引及标签
[objectindex, objectlabel] = deal([]);
for j = 1:length(sampleindex.trainindex)
    tmp = find( all(bsxfun(@eq, panseg, panseg(sampleindex.trainindex(j),:)),2) );
    objectindex = [objectindex;tmp];
    objectlabel = [objectlabel;ones(size(tmp))*Groundtruth(sampleindex.trainindex(j))];
end

% 如果两个训练样本位于一个对象内部，则它们所在的对象内的像素只需记为一次。
objectindex = unique([objectindex,objectlabel],'rows','stable');
objectlabel = objectindex(:,2);
objectindex = objectindex(:,1);

%% 初始化
nClass = length(unique(Groundtruth(:)))-1;
RESULT_CLA = cell(1,ppara.Maxiter+1);
[hyper_Prelabels,hyper_Preprobs] = deal(zeros(ppara.a, ppara.b,1),zeros(ppara.a * ppara.b,nClass));

% 迭代终止条件
[Last2Sw,Last1Sw,ThisSw] = deal(zeros(1,size(hyperdata,2)));
%初始标记样本
markedindex = sampleindex.trainindex;
markedlabel = Groundtruth(markedindex);

%% 进行主动学习迭代
for iter = 1:ppara.Maxiter
    if ~isfield(sampleindex,'validindex')
        [bestc,bestg] = GetcgCrossvalidate(hyperdata(markedindex,:),markedlabel,ppara.Nsets);
    else
        [bestc,bestg] = GetSVMcg(hyperdata(markedindex,:),markedlabel,...
            hyperdata(sampleindex.validindex,:),Groundtruth(sampleindex.validindex));
    end
    model = svmtrain(markedlabel,hyperdata(markedindex,:),['-c ',num2str(bestc),' -g ',num2str(bestg),' -b 1 -q']);
    % 计算分类精度
    [test_prelabel,~,~] = svmpredict(Groundtruth(sampleindex.testindex),hyperdata(sampleindex.testindex,:),model,'-b 1');
    RESULT_CLA{iter} = Confusmat(Groundtruth(sampleindex.testindex),test_prelabel);
    
    % 对象内部所有像素分类
    [object_Prelabels,~,object_Preprobs] = svmpredict(objectlabel,hyperdata(objectindex,:),model,'-b 1');
    hyper_Preprobs(objectindex,:) = object_Preprobs;
    hyper_Prelabels(objectindex) = object_Prelabels;
    
    % 选择分类标签与对象标签一致的样本
    candidateindex0 = objectindex(objectlabel==object_Prelabels);
    % 候选样本集中剔除已标记的样本,留下候选样本
    candidateindex0 = setdiff(candidateindex0,markedindex);
    candidatelabel0 = hyper_Prelabels(candidateindex0);
    
    %% 利用多样性准则优化候选样本集
    Selclass = round(ppara.select_per/nClass);
    [newmarkindex, newmarklabel] = deal([]);
    switch ppara.DiversityRule
        case 'SAM'
            %% 光谱角法
            [candidateindex,candidatelabel] = ALSelectfromCandidate(candidateindex0,candidatelabel0,...
                ppara.delta,hyper_Preprobs(candidateindex0,:),ppara.AL_method );
            for iClass = 1:nClass
                classcandindex = candidateindex(candidatelabel == iClass);
                if length(classcandindex) > Selclass
                    nSamples = MinSAMFind(hyperdata(classcandindex,:),Selclass,hyper_Preprobs(classcandindex,iClass));
                    newmarkindex = [newmarkindex;classcandindex(nSamples)];
                    newmarklabel = [newmarklabel;iClass*ones(Selclass,1)];
                else
                    newmarkindex = [newmarkindex;classcandindex;zeros(Selclass-length(classcandindex),1)];
                    newmarklabel = [newmarklabel;repmat(iClass,length(classcandindex),1);zeros(Selclass-length(classcandindex),1)];
                end
            end
        case 'K-SAM'
            %% 核余弦角法
            [candidateindex,candidatelabel] = ALSelectfromCandidate(candidateindex0,candidatelabel0,...
                ppara.delta,hyper_Preprobs(candidateindex0,:),ppara.AL_method );
            for iClass = 1:nClass
                classcandindex = candidateindex(candidatelabel == iClass);
                if length(classcandindex) > Selclass
                    nSamples = MaxDistFind(hyperdata(classcandindex,:),Selclass,hyper_Preprobs(classcandindex,iClass) );
                    newmarkindex = [newmarkindex;classcandindex(nSamples)];
                    newmarklabel = [newmarklabel;iClass*ones(Selclass,1)];
                else
                    newmarkindex = [newmarkindex;classcandindex;zeros(Selclass-length(classcandindex),1)];
                    newmarklabel = [newmarklabel;repmat(iClass,length(classcandindex),1);zeros(Selclass-length(classcandindex),1)];
                end
            end
        case 'CLUSTER'
            %% k均值聚类法
            [candidateindex,candidatelabel] = ALSelectfromCandidate(candidateindex0,candidatelabel0,...
                ppara.delta,hyper_Preprobs(candidateindex0,:),ppara.AL_method);
            for iClass = 1:nClass
                classcandindex = candidateindex(candidatelabel == iClass);
                if length(classcandindex) > Selclass
                    clusterLabel = kmeans( hyperdata(classcandindex,:),Selclass);%,'Options',statset('MaxIter',500) 
                    for kCluster = 1:Selclass
                        clustercandindex = classcandindex(clusterLabel == kCluster);
                        [~,nSamples] = min(max( hyper_Preprobs(clustercandindex,:) ,[],2));
                        newmarkindex = [newmarkindex;clustercandindex(nSamples)];
                    end
                    newmarklabel = [newmarklabel;iClass*ones(length(unique(clusterLabel)),1)];
                else
                    newmarkindex = [newmarkindex;classcandindex;zeros(Selclass-length(classcandindex),1)];
                    newmarklabel = [newmarklabel;repmat(iClass,length(classcandindex),1);zeros(Selclass-length(classcandindex),1)];
                end
            end
        case 'K-CLUSTER'
            %% 核k均值聚类法
            [candidateindex,candidatelabel] = ALSelectfromCandidate(candidateindex0,candidatelabel0,...
                ppara.delta,hyper_Preprobs(candidateindex0,:),ppara.AL_method);
            for iClass = 1:nClass
                classcandindex = candidateindex(candidatelabel == iClass);
                if length(classcandindex) > Selclass
                    clusterLabel = kernel_kmeans(hyperdata(classcandindex,:),Selclass,bestg);
                    for kCluster = 1:Selclass
                        clustercandindex = classcandindex(clusterLabel == kCluster);
                        [~,nSamples] = min(max( hyper_Preprobs(clustercandindex,:) ,[],2));
                        newmarkindex = [newmarkindex;clustercandindex(nSamples)];
                    end
                    newmarklabel = [newmarklabel;iClass*ones(length(unique(clusterLabel)),1)];
                else
                    newmarkindex = [newmarkindex;classcandindex;zeros(Selclass-length(classcandindex),1)];
                    newmarklabel = [newmarklabel;repmat(iClass,length(classcandindex),1);zeros(Selclass-length(classcandindex),1)];
                end
            end
        case 'SPA'
            %% 空间距离法
            [candidateindex,candidatelabel] = ALSelectfromCandidate(candidateindex0,candidatelabel0,...
                ppara.delta,hyper_Preprobs(candidateindex0,:),ppara.AL_method );
            for iClass = 1:nClass
                classcandindex = candidateindex(candidatelabel == iClass);
                if length(classcandindex) > Selclass
                    [x,y] = ind2sub([ppara.a,ppara.b],classcandindex);
                    nSamples = MaxDistFind([x(:),y(:)],Selclass,hyper_Preprobs(classcandindex,iClass) ); 
                    newmarkindex = [newmarkindex;classcandindex(nSamples)];
                    newmarklabel = [newmarklabel;iClass*ones(Selclass,1)];
                else
                    newmarkindex = [newmarkindex;classcandindex;zeros(Selclass-length(classcandindex),1)];
                    newmarklabel = [newmarklabel;repmat(iClass,length(classcandindex),1);zeros(Selclass-length(classcandindex),1)];
                end
            end
      
        otherwise
           %% 利用不确定性选择标记样本
            [newmarkindex,newmarklabel] = ALSelectfromCandidate(candidateindex0,hyper_Prelabels(candidateindex0),...
                ppara.select_per,hyper_Preprobs(candidateindex0,:),ppara.AL_method);
    end
    newmarkindex(newmarkindex == 0) = [];
    newmarklabel(newmarklabel == 0) = [];
    % 终止条件:所有测试样本的类内方差不发生改变
    if iter>2
        ThisSw = SumofSW(hyperdata(sampleindex.testindex,:),test_prelabel);
        if norm(ThisSw - Last1Sw)<=ppara.StopCond && norm(Last1Sw - Last2Sw)<=ppara.StopCond
            break;
        end
    end
    Last2Sw = Last1Sw;   Last1Sw = ThisSw;
    % 新标记样本加入到训练样本集中
    markedindex = [markedindex;newmarkindex];
    markedlabel = [markedlabel;newmarklabel];
end
for j = iter+1:20
    RESULT_CLA{j} = RESULT_CLA{iter};
end
if ppara.Mapping~= 0
    %迭代完成后对图像进行分类
    [hyper_Prelabels,~,~] = svmpredict(zeros(size(Groundtruth)),hyperdata,model,'-b 1');
    RESULT_CLA{iter+1}.hyper_Prelabels = hyper_Prelabels;
end

function nSamples = MinSAMFind(x,n,y)
if n >= size(x,1)
    nSamples = 1:size(x,1);
    return;
end
[~,nSamples] = min(y);
Scat(1,:) = x(nSamples,:);
for j = 2:n
    d = zeros(size(x,1),1);
    for k = 1:size(x,1)
        d(k) = sum( SpecAngleMatch(Scat,x(k,:)) );
    end
    d(nSamples) = 1;
    [~,nSamples(j)] = min(d);
    Scat(j,:) = x(nSamples(j),:);
end

function nSamples = MaxDistFind(x,n,y)
if n >= size(x,1)
    nSamples = 1:size(x,1);
    return;
end
[~,nSamples] = min(y);
Scat(1,:) = x(nSamples,:);
for j = 2:n
    d = zeros(size(x,1),1);
    for k = 1:size(x,1)
        d(k) = sum(sum((bsxfun(@minus,Scat,x(k,:))).^2));
    end
    d(nSamples) = 0;
    [~,nSamples(j)] = max(d);
    Scat(j,:) = x(nSamples(j),:);
end

function nSamples = MaxEntFind(x,n,y)
if n >= size(x,1)
    nSamples = 1:size(x,1);
    return;
end
[~,nSamples] = min(y);
Scat(1,:) = x(nSamples,:);
for j = 2:n
    H = zeros(size(x,1),1);
    for k = 1:size(x,1)
        temp = bsxfun(@times,Scat,y(k,:));
        temp = temp.*log(temp);
        H(k) = sum(-1*sum(temp,2));
    end
    H(nSamples) = 0;
    [~,nSamples(j)] = max(H);
    Scat(j,:) = x(nSamples(j),:);
end
