/*
  Reader.cpp
  
  Author: Sergi Diaz Planas (2017)
*/
#include "Reader.h"

// Constructor
Reader::Reader()
{
    this->_localDataPath = "";
}

// Return a string with the textual information read by tesseract
// OCR engine in the input image.
std::string Reader::read(Image image)
{
    // Return an empty string if given Image is empty or local data is at an 
    // unknown path.
    Mat mat = image.getImage();
    if (mat.empty() || this->_localDataPath == "")
        return "";

    std::string data;

    // Input image has to be preprocessed in order to improve
    // the performance of the OCR engine. Also, it has to be
    // converted into leptonica format.
    Mat preprocessed = preprocess(mat);
    Pix *lpImage; // leptonica image
    lpImage = mat8ToPix(preprocessed);

    // Init tesseract-ocr, without specifying tessdata path
    tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
    api->Init(this->_localDataPath.c_str(), "spa");

    // Extract data and terminate
    api->SetImage(lpImage);
    data = api->GetUTF8Text();
    api->End();
    pixDestroy(&lpImage);

    return data;
}

// Change the location of local data path.
void Reader::setLocalDataPath(std::string localDataPath)
{
    this->_localDataPath = localDataPath;
}

// Return a binarized copy of input image, making the OCR engine
// better at reading contained textual information. The binarization
// threshold is computed as half the average intensity among the pixels
// in the image after it has been grayscaled.
Mat Reader::preprocess(Mat image)
{
    cv::Mat image_gray;
    cv::Mat processed;

    // Convert to grayscale if necessary
    if (image.channels() == 3)
        cv::cvtColor(image, image_gray, CV_BGR2GRAY);
    else if(image.channels() == 4)
        cv::cvtColor(image, image_gray, CV_BGRA2GRAY);
    else
        image.copyTo(image_gray);

    // Compute average color
    long int avg = 0;
    for (int i=0; i<image_gray.rows; i++) {
        for (int j=0; j<image_gray.cols; j++) {
            avg += image_gray.at<unsigned char>(i, j);
        }
    }
    avg /= (image_gray.rows * image_gray.cols);

    // Binarize and return
    cv::threshold(image_gray, processed, 0.75 * avg, 255, cv::THRESH_BINARY);
    return processed;
}

// Convert an input cv matrix into leptonica format.
Pix * Reader::mat8ToPix(Mat mat)
{
    Pix *pixd = pixCreate(mat.size().width, mat.size().height, 8);
    for(int y=0; y<mat.rows; y++) {
        for(int x=0; x<mat.cols; x++) {
            pixSetPixel(pixd, x, y, (l_uint32) mat.at<uchar>(y,x));
        }
    }
    return pixd;
}
