function MakeMosaic(imgName, direcIn, smallSize, cost)
%
% MakeMosaic(imgName, direcIn, smallSize, cost)
%
% This function uses the images in 'direcIn' and tries to make a mosaic
% version of imgName using them (imgName is the filename of an image
% file). It shrinks the images in direcIn down to smallSize (say, [30
% by 30]) before making the mosaic of them, and uses the number in cost
% to penalize images repeating (larger cost == less repeats). On subsequent
% mosaics with the same image directory (direcIn), it runs very quickly
% since it caches the image's color information and keeps the small images
% around.
%
% Timothy F. Brady, tfbrady@mit.edu, Sept. 2008
%
    img = imread(imgName);
    fprintf('Making small images\n');
    direcOut  = [direcIn 'Small']; 
    MakeSmall(direcIn, direcOut, smallSize);
    fprintf(' -> Made small images\n\n');
    
    fprintf('Making color summary\n');
    colorsMat = SummaryColors(direcOut);
    fprintf(' -> Saved color summary\n\n');
    
    fprintf('Making best fits\n');
    newBest = BestFit(img, colorsMat, cost);
    save bestFits.mat newBest;
    fprintf(' -> Made best fits\n\n');
    
    fprintf('Making mosaic\n');
    mosaicImage = MakeMosaicFinal(direcOut, newBest, img, smallSize);    
    fprintf('\nWriting out mosaic\n');
    imwrite(mosaicImage, 'mosaic.jpg');             
    
    
%--------------------------------------------------------------------------    
function mosaicImage = MakeMosaicFinal(imgSrc, newBest, img, smallSize)        
% combine images into one big one
    lst = dir(imgSrc);
    mosaicImage = zeros(size(img,1)*smallSize(1), ...
                        size(img,2)*smallSize(2), ...
                        3);
    for i=1:size(newBest,1)
        for j=1:size(newBest,2)
            imgT = imread(fullfile(imgSrc, lst(newBest(i,j)).name));
            mosaicImage((i-1)*smallSize(1) + 1 : i*smallSize(1), ...
                        (j-1)*smallSize(2) + 1 : j*smallSize(2), ...
                        1:3) = ...
                        double(imgT(1:smallSize(1), 1:smallSize(2), 1:3)) ./ 255;
        end
        if mod(i,10)==0, fprintf(' - Row %d\n', i); end
    end    
    
%--------------------------------------------------------------------------    
function newBest = BestFit(img, colorsMat, costPerUse)        
% Min error fit of colors to pixels   
    newBest = zeros(size(img,1), size(img,2));
    costFunc = zeros(size(colorsMat,1), 1);
    randOrder = fullfact([size(img,1) size(img,2)]);
    whichR = Shuffle(1:size(randOrder));
    randOrderI = Shuffle(1:size(img,1));
    randOrderJ = Shuffle(1:size(img,2));
    for iW=1:length(whichR)
        cur = whichR(iW);
        i = randOrder(cur,1);
        j = randOrder(cur,2);
        curColor = squeeze(img(i,j,:))';
        curColorRep = repmat(curColor, [size(colorsMat,1) 1]);
        errMat = double(curColorRep) - colorsMat;
        errMat = sum(errMat .* errMat,2) + costFunc;
        [v, bestImg] = min(errMat); 
        newBest(i,j) = bestImg;
        costFunc(bestImg) = costFunc(bestImg) + costPerUse;
    end
    
%--------------------------------------------------------------------------
function colorsMat = SummaryColors(direcIn)
% Make small versions of everything in a directory
    if ~exist('colsList.mat')
        lst = dir(direcIn);
        for i=1:length(lst)
            colorsList{i,1} = [Inf Inf Inf];
            try
                imgT = imread(fullfile(direcIn, lst(i).name));
                colorsList{i,1} = squeeze(mean(mean(imgT,1),2))';
            end
        end
        colorsMat = cell2mat(colorsList);
        save('colsList.mat','colorsList','colorsMat');
    else
        load('colsList.mat');
    end
    
%--------------------------------------------------------------------------
function MakeSmall(direcIn, direcOut, sizeNew)
% Make small versions of everything in a directory
    if ~exist(direcOut)
        mkdir(direcOut);
        lst = dir(direcIn);
        for i=3:length(lst)
            try
                imgT = imread(fullfile(direcIn, lst(i).name));
                imgT = imresize(imgT, sizeNew);
                imwrite(imgT, fullfile(direcOut, lst(i).name));
            end
        end
    end

    
    