% LEARNING ACTIVE BASIS MODEL FROM NONALIGNED IMAGES
%% Load in exponential model, mex C codes, and set parameters
% ExponentialModel; % this line is not needed if 'storedExponentialModel' exists
clear; close all; 
load 'storedExponentialModel'; % load in exponential model 
mex CsharedSketch.c;  % learning from aligned images 
mex Csigmoid.c; % sigmoid transformation 
mex CgetMAX1.c; % getting MAX1 maps
mex CgetSUM2.c;  % detect object from multi-resolution images  
mex CdrawTemplate.c; % draw translated and deformed template
mex Ccopy.c; % copy around detect location
mex Cflip.c; % copy with flipping
epsilon = .1; % allowed correlation between selected Gabors 
locationShiftLimit = 3; % shift in normal direction = locationShiftLimit*subsample pixels
orientShiftLimit = 1; % shift in orientation
Correlation = CorrFilter(allFilter, epsilon); % correlation between filters 
numElement = 60; % number of Gabors in active basis   
numIteration = 10;  % number of iterations
rotateShiftLimit = 1; numRotate = 2*rotateShiftLimit + 1; % limit of rotation in learning
flipOrNot = 0; % left-right flip of the template, 0 or 1
numCluster = 2; % number of clusters
outputFolder = 'bearclusterRR';
if (exist([outputFolder 'eps']))
    delete([outputFolder 'eps/*.*']); 
else
    mkdir([outputFolder 'eps']); 
end
if (exist([outputFolder 'png']))
    delete([outputFolder 'png/*.*']); 
else
    mkdir([outputFolder 'png']); 
end
if (exist([outputFolder '.html']))
    delete([outputFolder '.html']); 
end
if (exist([outputFolder 'working']))
    delete([outputFolder 'working/*.*']); 
else
    mkdir([outputFolder 'working']); 
end
rangePercent = .1; % maximum percentage of image size shifted from center
%% Load in training images and initialize SUM1 maps for learning
%imageOption = 1; resizeFactor = .6; % resize images to specified ratio, and use the common upper-left portion 
imageOption = 2; sizex = 120; sizey = 120; % resize images to specified common height and width
%imageOption = 3; sizey = 100; % resize images to have common width without changing their aspect ratios, and use the common horizontal central line
imageFolder = 'positiveImage'; % folder of training images  
imageName = dir([imageFolder '/*.jpg']);
numImage = size(imageName, 1); % number of training images 
I = cell(1, numImage); 
LoadImage;
sizeTemplatex = sizex; sizeTemplatey = sizey; 
allSizex = zeros(1, numImage)+sizex; 
allSizey = zeros(1, numImage)+sizey; 
%% Compute SUM1 maps by Gabor filtering
disp(['start filtering']); tic
SUM1map0 = cell(numImage, numOrient); 
SUM1map0 = ApplyFilterfftSame(I, allFilter, localHalfx, localHalfy, -1, thresholdFactor); % SUM1 maps by Gabor filtering
Csigmoid(numImage, allSizex, allSizey, numOrient, saturation, SUM1map0);
disp(['filtering and sigmoid time: ' num2str(toc) ' seconds']);
%% Prepare output variables for learning
selectedOrient = zeros(1, numElement);  % orientation and location of selected Gabors
selectedx = zeros(1, numElement); 
selectedy = zeros(1, numElement); 
selectedlambda = zeros(1, numElement); % weighting parameter for scoring template matching
selectedLogZ = zeros(1, numElement); % normalizing constant
commonTemplate = single(zeros(sizeTemplatex, sizeTemplatey)); % template of active basis 
deformedTemplate = cell(1, numImage); % templates for training images 
for (img = 1 : numImage)
    deformedTemplate{img} = single(zeros(sizeTemplatex, sizeTemplatey));  
end
allSelectedx = zeros(numRotate, numElement); 
allSelectedy = zeros(numRotate, numElement); 
allSelectedOrient = zeros(numRotate, numElement); 
SUM1mapLearn = cell(numImage, numOrient); 
for (img = 1:numImage) 
    for (orient = 1:numOrient)
       SUM1mapLearn{img, orient} = single(zeros(sizeTemplatex, sizeTemplatey)); 
    end
end
%% initialize EM from random cluster
MAX2scoreAll = rand(numImage, numCluster);
MrotAll = zeros(numImage, numCluster); MflipAll = zeros(numImage, numCluster); 
MindAll = zeros(numImage, numCluster); MFxAll = zeros(numImage, numCluster); MFyAll = zeros(numImage, numCluster); 
for (c = 1 : numCluster)
    t = 0; 
    for (img = 1 : numImage)
       if (MAX2scoreAll(img, c) == max(MAX2scoreAll(img, :)))
           t = t + 1;
           for (orient = 1 : numOrient)
               SUM1mapLearn{t, orient} = SUM1map0{img, orient} + single(0.); 
           end
       end
    end
    disp(['**************** start learning template ' num2str(c)]); tic; 
    CsharedSketch(numOrient, locationShiftLimit, orientShiftLimit, ... % about active basis  
           numElement, numImage, t, sizeTemplatex, sizeTemplatey, SUM1mapLearn, ... % about training images 
           halfFilterSize, Correlation, allSymbol(1, :), ... % about filters
           numStoredPoint, storedlambda, storedExpectation, storedLogZ, ... % about exponential model 
           selectedOrient, selectedx, selectedy, selectedlambda, selectedLogZ, ... % learned parameters
           commonTemplate, deformedTemplate); % learned templates 
    disp(['mex-C learning time: ' num2str(toc) ' seconds']);
    Outepsgif(-double(commonTemplate), ['nonalign_sym0' num2str(c)]); 
    RotateTemplate;   
    templateName = [outputFolder 'working/template' 'cluster' num2str(c)];   
    save(templateName, 'commonTemplate', 'allSelectedOrient', 'allSelectedx', 'allSelectedy', 'selectedlambda', 'selectedLogZ', 'centerx', 'centery');                    
end
%% Generate multi-resolution images and initialize SUM1, MAX1 and SUM2 maps
numResolution = 5;  % number of resolutions to search for in detection stage
originalResolution = 3; % original resolution is the one at which the imresize factor = 1, see 11th line beneath this line 
allSizex = zeros(1, numResolution); allSizey = zeros(1, numResolution); 
ImageMultiResolution = cell(1, numResolution); 
MAX2score = single(zeros(1, numResolution));  % maximum log-likelihood score at each resolution
allFx = zeros(1, numResolution); allFy = zeros(1, numResolution); % detected location at each resolution   
MAX1map = cell(numResolution, numOrient);
SUM2map = cell(1, numResolution); 
translatedTemplate = cell(1, numResolution*numImage); 
for (img = 1:numImage)
  for(resolution=1:numResolution)
    resizeFactor = .8+(resolution-1)*.1; % so that .8+(originalResolution-1)*.1 = 1
    ImageMultiResolution{resolution} = imresize(I{img}, resizeFactor, 'nearest');  % images at multiple resolutions
    [sizex, sizey] = size(ImageMultiResolution{resolution}); 
    allSizex(resolution) = sizex; allSizey(resolution) = sizey; 
    for (orient = 1:numOrient) 
        MAX1map{resolution, orient} = single(zeros(sizex, sizey));
    end
    SUM2map{resolution} = single(zeros(sizex, sizey)); 
    translatedTemplate{resolution} = single(zeros(sizex, sizey));  
  end
  disp(['======> start filtering and maxing image ' num2str(img)]); tic
  SUM1mapFind = ApplyFilterfft(ImageMultiResolution, allFilter, localHalfx, localHalfy, thresholdFactor); % filtering images at multiple resolutions
  Csigmoid(numResolution, allSizex, allSizey, numOrient, saturation, SUM1mapFind);
  CgetMAX1(numResolution, allSizex, allSizey, numOrient,  ...
                     locationShiftLimit, orientShiftLimit, ...
                     SUM1mapFind, MAX1map);
  mapName = [outputFolder 'working/SUMMAXmap' 'image' num2str(img)];   
  save(mapName, 'ImageMultiResolution', 'SUM1mapFind', 'MAX1map', 'SUM2map', 'translatedTemplate', 'allSizex', 'allSizey');                
  disp(['filtering and maxing time: ' num2str(toc) ' seconds']);
end
%% Iteration part 1: detect the object in each image 
for(it = 1:numIteration)
    disp(['**************** detection for iteration ' num2str(it)]);
    for (img=1:numImage)
      disp(['======> start detecting in image ' num2str(img)]); 
      mapName = [outputFolder 'working/SUMMAXmap' 'image' num2str(img)]; 
      load(mapName); 
      for (c = 1 : numCluster) 
        disp(['matching template ' num2str(c)]); tic; 
        templateName = [outputFolder 'working/template' 'cluster' num2str(c)];   
        load(templateName);                    
        MMAX2 = -1e10; 
        for (flip = 0 : flipOrNot)
          for (rot = -rotateShiftLimit : rotateShiftLimit)
            r = rot+rotateShiftLimit+1 + (rotateShiftLimit*2+1)*flip;  
            CgetSUM2(numResolution, allSizex, allSizey, numOrient, ...
                     numElement, allSelectedOrient(r, :), allSelectedx(r, :), allSelectedy(r, :), selectedlambda, selectedLogZ, ...
                     MAX1map, SUM2map, MAX2score, allFx, allFy, rangePercent);    
            [maxOverResolution ind] = max(MAX2score);   % most likely resolution
            if (MMAX2 < maxOverResolution) 
               MMAX2 = maxOverResolution; Mrot = rot; Mflip = flip; 
               Mind = ind; MFx = allFx(ind); MFy = allFy(ind); 
            end
          end
        end
        MAX2scoreAll(img, c) = MMAX2; 
        MrotAll(img, c) = Mrot; MflipAll(img, c) = Mflip; 
        MindAll(img, c) = Mind; MFxAll(img, c) = MFx; MFyAll(img, c) = MFy; 
        disp(['mex-C finding time: ' num2str(toc) ' seconds']);
      end
    end
%% Iteration part 2: re-learn the template for each cluster
    for (c = 1 : numCluster)
      disp(['**************** re-learning template ' num2str(c) ' in iteration ' num2str(it)]); tic; 
      templateName = [outputFolder 'working/template' 'cluster' num2str(c)];   
      load(templateName);       
      t = 0; 
      for (img = 1 : numImage)
        if (MAX2scoreAll(img, c) == max(MAX2scoreAll(img, :)))
           mapName = [outputFolder 'working/SUMMAXmap' 'image' num2str(img)]; 
           load(mapName); 
           Mrot = MrotAll(img, c); Mflip = MflipAll(img, c); 
           Mind = MindAll(img, c); MFx = MFxAll(img, c); MFy = MFyAll(img, c); 
           t = t + 1; 
           for (orient = 1 : numOrient)
              orient1 = orient - Mrot; 
              if (orient1 > numOrient)
                 orient1 = orient1 - numOrient; 
              end
              if (orient1 <= 0)
                 orient1 = orient1 + numOrient; 
              end                    
             Ccopy(SUM1mapLearn{t, orient}, SUM1mapFind{Mind, orient1}, MFx, MFy, centerx, centery, sizeTemplatex, sizeTemplatey, allSizex(Mind), allSizey(Mind), Mrot*pi/numOrient);  
           end
           if (Mflip > 0)
             for (orient = 1 : numOrient)
                 SUM1mapLearnTmp{1, orient} = SUM1mapLearn{t, orient}+0.; 
             end
             for (orient = 1 : numOrient)
                 if (orient-1>0)
                     orient2 = numOrient - (orient-1) + 1; 
                 else
                     orient2 = orient;   
                 end
                 Cflip(SUM1mapLearn{t, orient2}, SUM1mapLearnTmp{1, orient}, sizeTemplatex, sizeTemplatey); 
             end
           end           
           if (it == numIteration)
              r = Mrot+rotateShiftLimit+1 + (rotateShiftLimit*2+1)*Mflip;  
              CdrawTemplate(numResolution, allSizex, allSizey, numOrient, ...
                locationShiftLimit, orientShiftLimit, ...
                halfFilterSize, SUM1mapFind, allSymbol(1, :), ...
                numElement, allSelectedOrient(r, :), allSelectedx(r, :), allSelectedy(r, :), ...
                translatedTemplate, Mind, MFx, MFy);
              Jshow{img} = ImageMultiResolution{Mind}+translatedTemplate{Mind}*100.;  % overlaid sketch 
           end
        end
      end
      CsharedSketch(numOrient, locationShiftLimit, orientShiftLimit, ... % about active basis  
           numElement, numImage, t, sizeTemplatex, sizeTemplatey, SUM1mapLearn, ... % about training images 
           halfFilterSize, Correlation, allSymbol(1, :), ... % about filters
           numStoredPoint, storedlambda, storedExpectation, storedLogZ, ... % about exponential model 
           selectedOrient, selectedx, selectedy, selectedlambda, selectedLogZ, ... % learned parameters
           commonTemplate, deformedTemplate); % learned templates 
    Outepsgif(-double(commonTemplate), ['nonalign_sym' 'iteration' num2str(it) 'cluster' num2str(c)]); 
    RotateTemplate;   
    templateName = [outputFolder 'working/template' 'cluster' num2str(c)];   
    save(templateName, 'commonTemplate', 'allSelectedOrient', 'allSelectedx', 'allSelectedy', 'selectedlambda', 'selectedLogZ', 'centerx', 'centery');                    
    disp(['mex-C learning time: ' num2str(toc) ' seconds']);
   end
end
%% save figures into eps and png folders  
  disp(['Displaying the clusters']);
  fid = fopen([outputFolder '.html'], 'wt');
  fprintf(fid, '%s\n', ['<a href="http://www.stat.ucla.edu/~ywu/AB/' outputFolder 'Code.zip">Code and data</a> <br>']);
  outMessage = ['==> Number of training images = ' num2str(numImage) ...
                        '; Image height and width = '  num2str(sizeTemplatex) ' by ' num2str(sizeTemplatey) ' pixels ' ...
                        '; Number of clusters = ' num2str(numCluster) ...
                        '; Number of elements = ' num2str(numElement) ...
                        '; Range of displacement = ' num2str(locationShiftLimit) ' pixels' ...
                        '; Number of iteration = ' num2str(numIteration) ...
                        '; Length of Gabor = ' num2str(halfFilterSize*2+1) ' pixels' ...      
                        '; Range of rotation of template = '  num2str(rotateShiftLimit) ' times '  ' pi/' num2str(numOrient)...
                        '<br>'];
  fprintf(fid, '%s\n', outMessage);
  disp(outMessage);                             
  fprintf(fid, '%s\n', ['<hr>']);
  heightstr0 = '"height=120> '; heightstr = '"height=80> '; length = 6; 
  for (c = 1:numCluster)      
    templateName = [outputFolder 'working/template' 'cluster' num2str(c)];   
    load(templateName);       
    pic = -double(commonTemplate); showImage; 
    saveas(gcf, [outputFolder 'eps/000template' num2str(100+c) '.eps'], 'eps');
    saveas(gcf, [outputFolder 'png/000template' num2str(100+c) '.png'], 'png');
    pic = commonTemplate*0.+255; showImage; 
    saveas(gcf, [outputFolder 'eps/empty.eps'], 'eps');
    saveas(gcf, [outputFolder 'png/empty.png'], 'png');   
    fprintf(fid, '%s\n', ['<IMG SRC="' outputFolder 'png/000template' num2str(100+c) '.png' heightstr0]);
    fprintf(fid, '%s\n', ['<br>']);
    MAX2scorec = MAX2scoreAll(:, c)+0.; 
    [MAX2scoresort, ind] = sort(MAX2scorec, 'descend'); 
    tt = 0; counter = 0; 
    for (i = 1 : numImage)
       this = ind(i); 
       if (MAX2scoreAll(this, c)==max(MAX2scoreAll(this, :)))
          tt = tt + 1; close all; 
          pic = I{this}; showImage; 
    saveas(gcf, [outputFolder 'eps/' num2str(100+c) 'I' num2str(1000+tt) '.eps'], 'eps');
    saveas(gcf, [outputFolder 'png/' num2str(100+c) 'I' num2str(1000+tt) '.png'], 'png');    
    pic = Jshow{this}; showImage; 
    saveas(gcf, [outputFolder 'eps/' num2str(100+c) 'I' num2str(1000+tt) 'sketch.eps'], 'eps');
    saveas(gcf, [outputFolder 'png/' num2str(100+c) 'I' num2str(1000+tt) 'sketch.png'], 'png');
   %% write the html code for reproducibility page
    fprintf(fid, '%s\n', ['<IMG SRC="' outputFolder 'png/' num2str(100+c) 'I' num2str(1000+tt) '.png' heightstr]);
    fprintf(fid, '%s\n', ['<IMG SRC="' outputFolder 'png/' num2str(100+c) 'I' num2str(1000+tt) 'sketch.png' heightstr]);
           counter = counter + 1; 
           if (counter == length)
               counter = 0; fprintf(fid, '%s\n', ['<br>']);
           end
       end %if
    end %img
    fprintf(fid, '%s\n', ['<hr>']); 
    end %c
    fclose(fid);
