https://github.com/rusen/CHOP
Tip revision: efe308b49fcb1e63205f67b7a4b631736d2f7c19 authored by rusen on 09 November 2014, 11:57:39 UTC
Merge branch 'master' of https://github.com/rusen/CHOP
Merge branch 'master' of https://github.com/rusen/CHOP
Tip revision: efe308b
runVocabularyLearning.m
%> Name: runVocabularyLearning
%>
%> Description: The entry function to CHOP code. Calls CHOP with
%> desired parameters on specified dataset. For each image in the dataset,
%> we create a graph out of level 1 gabor filter responses, and form
%> edges. The overall graph that consists of n (image count)
%> non-overlapping graphs is called "main graph" (how creative). The main
%> graph is compressed to obtain parts for the next level. Overlapping part
%> realizations in this new level are processed (so low-rank parts are
%> inhibited). The remaining part realizations contribute to the main graph
%> of the next level. This procedure lasts until no new parts are found.
%>
%> @param datasetName Name of the dataset to work on.
%> @param imageExtension The extension of the files to work on. Examples
%> include '.jpg', '.png', '_crop.png'...
%> @param imageExtension The ground truth image extension for each file.
%> For example, for a training image "swan.png", the GT image can have the
%> name "swan_gt.png", where imageExtension is '.png', and gtImageExtension
%> is '_gt.png'.
%>
%> Author: Rusen
%>
%> Updates
%> Ver 1.0 on 18.11.2013
%> Ver 1.1 on 05.12.2013 Various parameter additions, 'mode' changes
%> Ver 1.2 on 12.01.2014 Comment changes for unified code look
%> Ver 1.3 on 12.01.2014 Timing is added by Mete
%> Ver 1.4 on 17.02.2014 GT processing added.
%> Ver 1.5 on 16.06.2014 New comments, simplification on code.
function [] = runVocabularyLearning( datasetName, imageExtension, gtImageExtension )
%% ========== Step 0: Set program options and run initializations ==========
%% Step 0.0: Get program options and parameters.
options = SetParameters(datasetName, true);
datasetFolder = [options.currentFolder '/input/' datasetName '/vocab/'];
gtFolder = [options.currentFolder '/input/' datasetName '/gt/'];
processedFolder = options.processedFolder;
processedGTFolder = options.processedGTFolder;
% Open threads for parallel processing.
if options.parallelProcessing
s = matlabpool('size');
if s>0
matlabpool close;
end
matlabpool('open', options.numberOfThreads);
end
if options.learnVocabulary
% Create the folder structure required.
createFolders(options);
%% Step 0.1: Create initial data structures.
try
fileNames = fuf([datasetFolder '*', imageExtension], 1, 'detail');
catch
display(['Unable to find training images. Possibly you forgot to put the images under ./input/' datasetName '/vocab/ or defined the extension wrong.']);
return;
end
trainingFileNames = fileNames;
%% Step 0.2: Allocate space to keep names of corresponding gt files.
gtFileNames = cell(numel(trainingFileNames),1);
%% ========== Step 1: Pre-process the data (extract first level nodes, surpress weak responses) ==========
%% Step 1.0: Pre-processing. Downsample images, and associate them with their ground truth.
maxImageDim = options.maxImageDim;
for fileItr = 1:size(trainingFileNames,1)
% Read image and downsample it.
img = imread(trainingFileNames{fileItr});
[~, fileName, ~] = fileparts(trainingFileNames{fileItr});
if max(size(img)) > maxImageDim
img = imresize(img, maxImageDim/max(size(img)), 'bilinear');
end
% If img is binary, we can save it as a binary png.
if size(img,3) == 1 && max(max(max(img))) == 1
img = img > 0;
end
% Save image into processed folder.
fileNameNew = fileName;
if exist([processedFolder '/' fileName '.png'], 'file')
fileId = 1;
fileNameNew = [fileName '_' num2str(fileId)];
while exist([processedFolder '/' fileNameNew '.png'], 'file')
fileId = fileId + 1;
fileNameNew = [fileName '_' num2str(fileId)];
end
end
imwrite(img, [processedFolder '/' fileNameNew '.png']);
% Switch file names with those copied.
trainingFileNames(fileItr) = {[processedFolder '/' fileNameNew '.png']};
% If gt file exists, write it to the processed gt folder. In
% addition, we keep track of the name of gt file corresponding
% to the image read.
gtFile = [gtFolder fileName gtImageExtension];
if exist(gtFile, 'file')
gtImg = imread(gtFile);
if size(gtImg,3)>2
gtImg = rgb2gray(gtImg(:,:,1:3))>0;
else
gtImg = gtImg(:,:,1)>0;
end
gtImg = imresize(gtImg, [size(img,1), size(img,2)], 'bilinear');
gtFileNames(fileItr) = {[processedGTFolder '/' fileName '.png']};
imwrite(gtImg, [processedGTFolder '/' fileName '.png']);
end
end
%% Step 1.1: Extract a set of features from the input images.
display('..... Level 1 Node Extraction started. This may take a while.');
allNodes = cell(size(trainingFileNames,1),1);
smoothedFolder = options.smoothedFolder;
parfor fileItr = 1:size(trainingFileNames,1)
[~, fileName, ~] = fileparts(trainingFileNames{fileItr});
img = imread([processedFolder '/' fileName '.png']);
% Get the Level 1 features.
[nodes, smoothedImg] = getNodes(img, gtFileNames{fileItr}, options);
% Keep nodes in the array.
allNodes(fileItr) = {nodes};
% Save smoothed image.
imwrite(smoothedImg, [smoothedFolder '/' fileName '.png']);
end
% Reorder images based on their node count. This helps in
% efficient parallelization.
nodeCounts = cellfun(@(x) size(x,1), allNodes);
[~, sortedImageIdx] = sort(nodeCounts, 'descend');
trainingFileNames = trainingFileNames(sortedImageIdx);
allNodes = allNodes(sortedImageIdx);
%% Learn category/pose of each image and and correct their order
% based on the sort order of the images.
categoryArr = cell(numel(trainingFileNames),1);
poseArr = zeros(numel(trainingFileNames),1);
for fileItr = 1:numel(fileNames)
% Get category label.
fullName = fileNames{fileItr};
[~, fileName, ext] = fileparts(fileNames{fileItr});
strLength = numel([options.currentFolder '/input/' options.datasetName '/vocab/']);
fileNameLength = numel(ext) + numel(fileName) + 1; % 1 for '/' (folder delimiter)
if numel(fullName) >= strLength + fileNameLength
categoryStr = fullName(1, (strLength+1):(end-fileNameLength));
categoryStrSepIdx = strfind(categoryStr, '/');
if ~isempty(categoryStrSepIdx)
categoryStr = categoryStr(:, 1:(categoryStrSepIdx(1)-1));
end
categoryArr(fileItr) = {categoryStr};
else
categoryArr(fileItr) = {''};
end
% Get pose info.
poseStartIdx = strfind(fileName, '_r');
if ~isempty(poseStartIdx)
poseIdx = poseStartIdx + numel('_r');
poseArr(fileItr) = sscanf(fileName(1, poseIdx:end), '%d');
end
end
categoryArr = categoryArr(sortedImageIdx);
poseArr = poseArr(sortedImageIdx); %#ok<NASGU>
% Set image ids for each node.
imageIds = cell(size(allNodes,1),1);
for fileItr = 1:size(allNodes,1)
imageIds(fileItr) = {num2cell(repmat(fileItr, size(allNodes{fileItr},1), 1))};
end
% Convert all node data into a single matrix.
allNodes = cat(1, allNodes{:});
imageIds = cat(1, imageIds{:});
leafNodes = [allNodes, imageIds];
% Learn node signs based on whether or not they are from the
% background class. The ones which are from the background class
% will have negative signs, while all others have positive signs.
% The effect is that in Subdue, subgraphs that compress positive
% graphs but not negative ones will be favoured.
imageSigns = ~strcmp(categoryArr, options.backgroundClass);
leafNodeSigns = imageSigns(cell2mat(imageIds));
%% ========== Step 2: Create first-level object graphs, and print them to a file. ==========
[vocabLevel, graphLevel] = generateLevels(leafNodes, leafNodeSigns, options);
%% Step 2.1: Get first-level object graph edges.
mainGraph = {graphLevel};
[modes, mainGraph] = extractEdges(mainGraph, options, 1, []);
graphLevel = mainGraph{1};
%% ========== Step 3: Create compositional vocabulary (Main loop in algorithm 1 of ECCV 2014 paper). ==========
tr_s_time=tic;
[vocabulary, redundantVocabulary, mainGraph, modes, distanceMatrices] = learnVocabulary(vocabLevel, graphLevel, leafNodes(:,1:3), modes, ...
options, trainingFileNames); %#ok<NASGU,ASGLU>
tr_stop_time=toc(tr_s_time); %#ok<NASGU>
% Export realizations into easily-readable arrays.
exportArr = exportRealizations(mainGraph); %#ok<NASGU>
% Transform category array into an index-based one (having numbers
% instead of category strings). The category string labels is saved
% in categoryNames.
[~, categoryNames, categoryArrIdx] = unique(categoryArr, 'stable'); %#ok<NASGU>
categoryNames = categoryArr(categoryNames); %#ok<NASGU>
% Print everything to files.
save([options.currentFolder '/output/' datasetName '/trtime.mat'], 'tr_stop_time');
save([options.currentFolder '/output/' datasetName '/vb.mat'], 'vocabulary', 'redundantVocabulary', 'modes', 'trainingFileNames', 'categoryNames');
% categoryArr is kept for backward-compatibility. It will be
% removed in further releases.
save([options.currentFolder '/output/' datasetName '/export.mat'], 'trainingFileNames', 'exportArr', 'categoryArr', 'categoryArrIdx', 'poseArr');
end
% Close thread pool if opened.
if options.parallelProcessing
matlabpool close;
end
end