function [score_total,execution_time_total,num_correct_bits,num_false_alarms,num_repeats,execution_time] = evaluate
%
% evaluate.m for EE368 project, Spring 2006
%
% This script calls your own detection routine and compares the results with the ground truth
% data to evaluate the performance of your routine. You can use this program to evaluate how 
% well your routine works with the provided training images. You are also encouraged to generate
% your own training images with the provided generate_code.m to make your detection routine more 
% robust.
% 
% The detection routine should be named as detect_code.m and put in the same directory as
% evaluate.m. Before the project due date, you need to submit your own detect_code.m and all the
% auxiliary files. The teaching staff will test your detection routine using this evaluate.m with
% a different set of images on the Linux machines in the SCIEN Lab. It is also required that your
% routine returns results within 1 minute for each image when running on the SCIEN Linux machines.
% Therefore, please make sure your routine works properly on those machines before submission.
%
% The detection routine should be defined as:
%    function [data_detected,origin_detected] = detect_code(img)
%
% The input and output are defined as follows
%    img: 
%      height-by-width-by-3 uint8 array representing a 24-bit RGB image obtained through
%      imread()
%    data_detected:
%      N-by-83 double array of 0's and 1's where N is the number of code markers detected. Each
%      row of data_detected corresponds to the 83-bit data embedded in the code.
%    origin_detected:
%      N-by-2 double array where N is the number of code markers detected. Each row of origin_detected
%      corresponds to the [row,column] image-coordinate of the origin of a detected code marker. The
%      origin of a code is defined as the center of the element at the top-left corner of the code,
%      i.e., code(1,1) defined in generate_code.m.
%
% For each code in an image, if there is only one detected code marker with the detected origin within a
% radius of 10 pixels (Euclidian distance in the image) to the ground-truth origin, this detected
% code marker is considered a "hit". If there are multiple detected code markers within the radius, only the 
% detected code marker with the detected origin closest to the ground-truth origin is considered a "hit".
% Every other detected code marker within the radius is considered a "repeat". A detected code marker with a
% detected origin that does not fall within a 10-pixel radius to any of the ground-truth origins
% is considered a "false-alarm".
%
% For each "hit", data_detected is compared with the associated ground truth value and the number
% of correctly detected bits (out of 83 bits) is calculated and accumulated over possibly multiple 
% hits (when there are multiple codes in the image).
% 
% The score of your detection routine for a single image is calculated as
%    score = number of correctly detected bits
%            - floor(83/2) * (number of false alarms + number of repeats)
% Negative scores are clipped to zero. The score will also be set to zero if the detection routine
% takes more than 1 minute for this image. The total score, score_total, is caculated by summing up
% the scores over the images in the data set.
%
% The performance will be ranked among project groups according to the following criteria:
%    (1) score_total, the higher the better
%    (2) If there is a tie in (1), consider execution_time_total, the smaller the better


GROUND_TRUTH_NAME = 'training_ground_truth'; % ground truth data
IMAGE_NAME = 'training';
NUM_IMAGES = 12; % number of images in the data set

NUM_BITS_IN_CODE = 83; % number of data bits embedded in a code
MAX_DISTANCE = 10; % the radius for a detected code to be considered a hit
MAX_TIME = 60; % maximum allowable execution time for an image (in seconds)

load(GROUND_TRUTH_NAME);

num_correct_bits = zeros(1,NUM_IMAGES); % number of bits in each image that are correctly detected
num_false_alarms = zeros(1,NUM_IMAGES); % number of false alarms for each image
num_repeats = zeros(1,NUM_IMAGES); % number of repeats for each image

score = zeros(1,NUM_IMAGES); % score for each image
execution_time = zeros(1,NUM_IMAGES); % execution time of your detection routine for each image

for i = 1:NUM_IMAGES
    
    % read the image and the ground truth data
    img = imread(sprintf('%s_%d.jpg',IMAGE_NAME,i));    
    eval(sprintf('data_true = data_true_%d;',i));
    eval(sprintf('origin_true = origin_true_%d;',i));
    
    % execute your detection routine
    % tic;
    t0 = clock;
    
    [data_detected,origin_detected] = detect_code(img);
    % For example, if your routine works perfectly you should get
    % data_detected = data_true;
    % origin_detected = origin_true;    
    
    % execution_time(i) = toc;
    execution_time(i) = etime(clock,t0);
    
    if (execution_time(i)>MAX_TIME) | isempty(data_detected) | isempty(origin_detected)
        continue; % score for this image is zero
    end
    
    % make sure the dimensions of data_detected and origin_detected are consistent
    if (size(data_detected,1)~=size(origin_detected,1))
        data_detected = data_detected(1:min([size(data_detected,1),size(origin_detected,1)]),:);
        origin_detected = origin_detected(1:min([size(data_detected,1),size(origin_detected,1)]),:);
    end
    
    num_codes_true = size(data_true,1);
    num_codes_detected = size(data_detected,1);
        
    % determine a detected code is a false alarm (type 0), a hit (type 1), or a repeat (type 2)
    code_type = zeros(1,num_codes_detected);
    for j = 1:num_codes_true        
        distance = sqrt(sum((repmat(origin_true(j,:),[num_codes_detected 1])-origin_detected).^2,2));
        [sorted_dist,sorted_idx] = sort(distance);
        
        num_detected_in_radius = sum(sorted_dist<=MAX_DISTANCE);
        if num_detected_in_radius>0
            code_type(sorted_idx(1)) = 1;
            code_type(sorted_idx(2:num_detected_in_radius)) = 2;
            
            num_correct_bits(i) = num_correct_bits(i) ...
                + sum(data_detected(sorted_idx(1),:)==data_true(j,:));
        end
    end

    num_false_alarms(i) = length(find(code_type==0));
    num_repeats(i) = length(find(code_type==2));
    
    score(i) = num_correct_bits(i) - floor(NUM_BITS_IN_CODE/2)*(num_false_alarms(i)+num_repeats(i));
    score(i) = max([score(i) 0]);
end

score_total = sum(score);
execution_time_total = sum(execution_time);