function  encodeImage(encoder, im, varargin)
% ENCODEIMAGE   Apply an encoder to images
%   IM is a cell array of the path to images.
%
%   ENCODEIMAGE(ENCODER, IM, CACHE) utilizes the specified CACHE
%   directory to store encodings for the given images. 
%
%   See also: TRAINENCODER().

opts.cacheDir = [] ;
opts.cacheChunkSize = 512 ;
opts = vl_argparse(opts,varargin) ;

if ~iscell(im), im = {im} ; end

% break the computation into cached chunks %
startTime = tic ;
numChunks = ceil(numel(im) / opts.cacheChunkSize) ;
for c = 1:numChunks
  n = min(opts.cacheChunkSize, numel(im) - (c-1)*opts.cacheChunkSize) ;
  chunkPath = fullfile(opts.cacheDir, sprintf('chunk-%03d.mat',c)) ;
  if ~isempty(opts.cacheDir) && exist(chunkPath, 'file')
     fprintf('%s: skipping %s\n', mfilename, chunkPath) ;
  else
    range = (c-1)*opts.cacheChunkSize + (1:n) ;
    fprintf('%s: processing a chunk of %d images (%3d of %3d, %5.1fs to go)\n', ...
      mfilename, numel(range), c, numChunks, toc(startTime) / (c-1) * (numChunks-c+1)) ;
    data = processChunk(encoder, im(range)) ;
    data = single(data);
    data = bsxfun(@times, data, 1./(sqrt(sum(data.^2))+eps)) ;
    if ~isempty(opts.cacheDir)
      save(chunkPath, 'data', '-v7.3') ;
    end
  end
  clear data ;
end

% --------------------------------------------------------------------
function psi = processChunk(encoder, im)
% --------------------------------------------------------------------
psi = cell(1,numel(im)) ;
if numel(im) > 1 && matlabpool('size') > 1
  parfor i = 1:numel(im) 
    psi{i} = encodeOne(encoder, im{i}) ;
  end
else
  % avoiding parfor makes debugging easier %
  for i = 1:numel(im)
    psi{i} = encodeOne(encoder, im{i}) ;
  end
end
psi = cat(2, psi{:}) ;


% --------------------------------------------------------------------
function psi = encodeOne(encoder, Im)
% --------------------------------------------------------------------
im = encoder.readImageFn(Im) ;
features = encoder.extractorFn(im) ;
imageSize = size(im) ;
psi = {} ;

% encoding all descrs %
[code_matrix, soft_idx] = lasc_encode(features.descr, encoder); 

% applying spacial pyramid and pooling %
for i = 1:size(encoder.subdivisions,2)
  minx = encoder.subdivisions(1,i) * imageSize(2) ;
  miny = encoder.subdivisions(2,i) * imageSize(1) ;
  maxx = encoder.subdivisions(3,i) * imageSize(2) ;
  maxy = encoder.subdivisions(4,i) * imageSize(1) ;
  ok = ...
    minx <= features.frame(1,:) & features.frame(1,:) < maxx  & ...
    miny <= features.frame(2,:) & features.frame(2,:) < maxy ;

  idx_patch = soft_idx(ok,:);
  code_patch.first = code_matrix.first (:,ok); 
  code_patch.second = code_matrix.second (:,ok); 
  z = single(lasc_pooling(encoder, idx_patch, code_patch, sum(encoder.pca.pcaNum)*2));
  z = z /(norm(z)+eps) ;
%   z = encoder.subdivisions(5,i) * z;
  psi{i} = z(:) ;
end
psi = cat(1, psi{:}) ;


