<commit_before>/*
 * Copyright (c) 2015 New Designs Unlimited, LLC
 * Opensource Automated License Plate Recognition [http://www.openalpr.com]
 *
 * This file is part of OpenAlpr.
 *
 * OpenAlpr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "alpr_impl.h"

void plateAnalysisThread(void* arg);

using namespace std;
using namespace cv;

namespace alpr
{
  AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
  {
    config = new Config(country, configFile, runtimeDir);
    
    plateDetector = ALPR_NULL_PTR;
    stateIdentifier = ALPR_NULL_PTR;
    ocr = ALPR_NULL_PTR;
    
    // Config file or runtime dir not found.  Don't process any further.
    if (config->loaded == false)
    {
      return;
    }

    plateDetector = createDetector(config);
    ocr = new OCR(config);
    setNumThreads(0);

    setDetectRegion(DEFAULT_DETECT_REGION);
    this->topN = DEFAULT_TOPN;
    setDefaultRegion("");

  }
  AlprImpl::~AlprImpl()
  {
    delete config;

    if (plateDetector != ALPR_NULL_PTR)
      delete plateDetector;

    if (stateIdentifier != ALPR_NULL_PTR)
      delete stateIdentifier;

    if (ocr != ALPR_NULL_PTR)
      delete ocr;
  }

  bool AlprImpl::isLoaded()
  {
    return config->loaded;
  }


  AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
  {
    timespec startTime;
    getTime(&startTime);


    AlprFullDetails response;

    response.results.epoch_time = getEpochTime();
    response.results.img_width = img.cols;
    response.results.img_height = img.rows;

    for (unsigned int i = 0; i < regionsOfInterest.size(); i++)
    {
      response.results.regionsOfInterest.push_back(AlprRegionOfInterest(regionsOfInterest[i].x, regionsOfInterest[i].y,
              regionsOfInterest[i].width, regionsOfInterest[i].height));
    }

    if (!img.data)
    {
      // Invalid image
      if (this->config->debugGeneral)
        std::cerr << "Invalid image" << std::endl;

      return response;
    }

    // Find all the candidate regions
    if (config->skipDetection == false)
    {
      response.plateRegions = plateDetector->detect(img, regionsOfInterest);
    }
    else
    {
      // They have elected to skip plate detection.  Instead, return a list of plate regions
      // based on their regions of interest
      for (unsigned int i = 0; i < regionsOfInterest.size(); i++)
      {
        PlateRegion pr;
        pr.rect = cv::Rect(regionsOfInterest[i]);
        response.plateRegions.push_back(pr);
      }
    }

    queue<PlateRegion> plateQueue;
    for (unsigned int i = 0; i < response.plateRegions.size(); i++)
      plateQueue.push(response.plateRegions[i]);

    int platecount = 0;
    while(!plateQueue.empty())
    {
      PlateRegion plateRegion = plateQueue.front();
      plateQueue.pop();

      PipelineData pipeline_data(img, plateRegion.rect, config);

      timespec platestarttime;
      getTime(&platestarttime);

      LicensePlateCandidate lp(&pipeline_data);

      lp.recognize();

      bool plateDetected = false;
      if (pipeline_data.plate_area_confidence > 10)
      {
        AlprPlateResult plateResult;
        plateResult.region = defaultRegion;
        plateResult.regionConfidence = 0;
        plateResult.plate_index = platecount++;

        for (int pointidx = 0; pointidx < 4; pointidx++)
        {
          plateResult.plate_points[pointidx].x = (int) pipeline_data.plate_corners[pointidx].x;
          plateResult.plate_points[pointidx].y = (int) pipeline_data.plate_corners[pointidx].y;
        }

        if (detectRegion)
        {
          stateIdentifier->recognize(&pipeline_data);
          if (pipeline_data.region_confidence > 0)
          {
            plateResult.region = pipeline_data.region_code;
            plateResult.regionConfidence = (int) pipeline_data.region_confidence;
          }
        }



        ocr->performOCR(&pipeline_data);
        ocr->postProcessor.analyze(plateResult.region, topN);
        const vector<PPResult> ppResults = ocr->postProcessor.getResults();


        int bestPlateIndex = 0;

        for (unsigned int pp = 0; pp < ppResults.size(); pp++)
        {
          if (pp >= topN)
            break;

          // Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match
          if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate)
            bestPlateIndex = pp;

          if (ppResults[pp].letters.size() >= config->postProcessMinCharacters &&
            ppResults[pp].letters.size() <= config->postProcessMaxCharacters)
          {
            AlprPlate aplate;
            aplate.characters = ppResults[pp].letters;
            aplate.overall_confidence = ppResults[pp].totalscore;
            aplate.matches_template = ppResults[pp].matchesTemplate;
            plateResult.topNPlates.push_back(aplate);
          }
        }

        if (plateResult.topNPlates.size() > 0)
          plateResult.bestPlate = plateResult.topNPlates[bestPlateIndex];

        timespec plateEndTime;
        getTime(&plateEndTime);
        plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime);

        if (plateResult.topNPlates.size() > 0)
        {
          plateDetected = true;<commit_msg>Fixed index out of bounds issue on plates w/ pattern that have elements that are < minimum characters<commit_after>/*
 * Copyright (c) 2015 New Designs Unlimited, LLC
 * Opensource Automated License Plate Recognition [http://www.openalpr.com]
 *
 * This file is part of OpenAlpr.
 *
 * OpenAlpr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "alpr_impl.h"

void plateAnalysisThread(void* arg);

using namespace std;
using namespace cv;

namespace alpr
{
  AlprImpl::AlprImpl(const std::string country, const std::string configFile, const std::string runtimeDir)
  {
    config = new Config(country, configFile, runtimeDir);
    
    plateDetector = ALPR_NULL_PTR;
    stateIdentifier = ALPR_NULL_PTR;
    ocr = ALPR_NULL_PTR;
    
    // Config file or runtime dir not found.  Don't process any further.
    if (config->loaded == false)
    {
      return;
    }

    plateDetector = createDetector(config);
    ocr = new OCR(config);
    setNumThreads(0);

    setDetectRegion(DEFAULT_DETECT_REGION);
    this->topN = DEFAULT_TOPN;
    setDefaultRegion("");

  }
  AlprImpl::~AlprImpl()
  {
    delete config;

    if (plateDetector != ALPR_NULL_PTR)
      delete plateDetector;

    if (stateIdentifier != ALPR_NULL_PTR)
      delete stateIdentifier;

    if (ocr != ALPR_NULL_PTR)
      delete ocr;
  }

  bool AlprImpl::isLoaded()
  {
    return config->loaded;
  }


  AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest)
  {
    timespec startTime;
    getTime(&startTime);


    AlprFullDetails response;

    response.results.epoch_time = getEpochTime();
    response.results.img_width = img.cols;
    response.results.img_height = img.rows;

    for (unsigned int i = 0; i < regionsOfInterest.size(); i++)
    {
      response.results.regionsOfInterest.push_back(AlprRegionOfInterest(regionsOfInterest[i].x, regionsOfInterest[i].y,
              regionsOfInterest[i].width, regionsOfInterest[i].height));
    }

    if (!img.data)
    {
      // Invalid image
      if (this->config->debugGeneral)
        std::cerr << "Invalid image" << std::endl;

      return response;
    }

    // Find all the candidate regions
    if (config->skipDetection == false)
    {
      response.plateRegions = plateDetector->detect(img, regionsOfInterest);
    }
    else
    {
      // They have elected to skip plate detection.  Instead, return a list of plate regions
      // based on their regions of interest
      for (unsigned int i = 0; i < regionsOfInterest.size(); i++)
      {
        PlateRegion pr;
        pr.rect = cv::Rect(regionsOfInterest[i]);
        response.plateRegions.push_back(pr);
      }
    }

    queue<PlateRegion> plateQueue;
    for (unsigned int i = 0; i < response.plateRegions.size(); i++)
      plateQueue.push(response.plateRegions[i]);

    int platecount = 0;
    while(!plateQueue.empty())
    {
      PlateRegion plateRegion = plateQueue.front();
      plateQueue.pop();

      PipelineData pipeline_data(img, plateRegion.rect, config);

      timespec platestarttime;
      getTime(&platestarttime);

      LicensePlateCandidate lp(&pipeline_data);

      lp.recognize();

      bool plateDetected = false;
      if (pipeline_data.plate_area_confidence > 10)
      {
        AlprPlateResult plateResult;
        plateResult.region = defaultRegion;
        plateResult.regionConfidence = 0;
        plateResult.plate_index = platecount++;

        for (int pointidx = 0; pointidx < 4; pointidx++)
        {
          plateResult.plate_points[pointidx].x = (int) pipeline_data.plate_corners[pointidx].x;
          plateResult.plate_points[pointidx].y = (int) pipeline_data.plate_corners[pointidx].y;
        }

        if (detectRegion)
        {
          stateIdentifier->recognize(&pipeline_data);
          if (pipeline_data.region_confidence > 0)
          {
            plateResult.region = pipeline_data.region_code;
            plateResult.regionConfidence = (int) pipeline_data.region_confidence;
          }
        }



        ocr->performOCR(&pipeline_data);
        ocr->postProcessor.analyze(plateResult.region, topN);
        const vector<PPResult> ppResults = ocr->postProcessor.getResults();


        int bestPlateIndex = 0;

        for (unsigned int pp = 0; pp < ppResults.size(); pp++)
        {
          if (pp >= topN)
            break;

          if (ppResults[pp].letters.size() >= config->postProcessMinCharacters &&
            ppResults[pp].letters.size() <= config->postProcessMaxCharacters)
          {
            // Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match
            if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate)
              bestPlateIndex = plateResult.topNPlates.size();
            
            AlprPlate aplate;
            aplate.characters = ppResults[pp].letters;
            aplate.overall_confidence = ppResults[pp].totalscore;
            aplate.matches_template = ppResults[pp].matchesTemplate;
            plateResult.topNPlates.push_back(aplate);
          }
        }

        if (plateResult.topNPlates.size() > bestPlateIndex)
        {
          AlprPlate bestPlate;
          bestPlate.characters = plateResult.topNPlates[bestPlateIndex].characters;
          bestPlate.matches_template = plateResult.topNPlates[bestPlateIndex].matches_template;
          bestPlate.overall_confidence = plateResult.topNPlates[bestPlateIndex].overall_confidence;
          
          plateResult.bestPlate = bestPlate;
        }

        timespec plateEndTime;
        getTime(&plateEndTime);
        plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime);

        if (plateResult.topNPlates.size() > 0)
        {
          plateDetected = true;
{'fraction_non_alphanumeric': 0.09931816314795038, 'fraction_numerical': 0.0033681097510884746, 'mean_word_length': 2.7632148377125194, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6582572', 'n_tokens_mistral': 3708, 'n_tokens_neox': 3502, 'n_words': 861}