/*
 * Copyright 2016 Jan Dorazil <deu439@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "evaluate.hpp"

#include <opencv2/opencv.hpp>

using namespace cv;

void Evaluate::evaluate(cv::Mat groundTruth, cv::Mat mask )
{
    for(int row = 0; row < groundTruth.rows; row++) {
        uint8_t* pixelA = groundTruth.ptr<uint8_t>(row);
        uint8_t* pixelB = mask.ptr<uint8_t>(row);

        for(int col = 0; col < groundTruth.cols; col++) {
            if(pixelA[col] == 0xff ) {
                if(pixelB[col] != 0xff)  fn++;
                if(pixelB[col] == 0xff ) tp++;
            }
            if(pixelA[col] != 0xff ) {
                if(pixelB[col] != 0xff) tn++;
                if(pixelB[col] == 0xff) fp++;
            }
        }
    }
}

void Evaluate::calculate( float &precision, float &recall, float &fmeas)
{
    if( tp == 0 ) {
        recall = 0;
        precision = 0;
        fmeas = 0;
    }
    else {
        recall = (float)tp / (tp + fn);
        precision = (float)tp / (tp + fp);
        fmeas = (2*precision*recall) / (precision+recall);
    }
}

void Evaluate::process(DataUnit& data)
{
  
  if( mInc ){
        evaluate(data.mMat[GTRU], data.mMat[OUTP]);
  } else {
    // Clear the parameters
    tp = 0; tn = 0; fp = 0; fn = 0;
    evaluate(data.mMat[GTRU], data.mMat[OUTP]);
    
    // Calculate metrics
    float precision, recall,  fmeas;
    calculate(precision, recall, fmeas);
        
    // Draw performance metrics to the mask
    std::ostringstream stream;
    stream.precision(3);
    stream << "precision: " << precision;
    stream << ", recall: " << recall;
    stream << ", f-measure: " << fmeas;
    
    putText(data.mMat[OUTP], stream.str(),
            Point(15, 20), 
            CV_FONT_HERSHEY_PLAIN, 1,
            Scalar(128), 1, 8, false);
  }

  // Incorporate ground truth into the mask
  data.mMat[OUTP] = data.mMat[OUTP] + (data.mMat[GTRU] / 2);
}

void Evaluate::setInc(bool inc)
// If this flag is set the metrics are not drawn into the image and can be 
// later received with calculate() method. Metrics will be computed for all 
// processed data units.
{
  mInc = inc;
}

