#include "TrainAnaEngine.h"

#define SHOW_RECTANGLE

using namespace ai_matrix;

unsigned int num=0;

TrainAnaEngine::TrainAnaEngine() {}

TrainAnaEngine::~TrainAnaEngine() {}

APP_ERROR TrainAnaEngine::Init()
{
    LogInfo << "TrainAnaEngine Init start";
    // 初始化相机帧率
    nFrameRate = FRAME_RATE;
    nRecIndex = 0;
    nPicAreaChangeing = 0x00;
    
    strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";
    strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1";
    dataSourceConfig_ = MyYaml::GetIns()->GetDataSourceConfigById(engineId_);
    strResultPath_ = MyYaml::GetIns()->GetPathValue("gc_result_path");
    strResultPath_for_test = MyYaml::GetIns()->GetPathValue("gc_result_path_for_test");

    // 获取算法参数的文件路径
    std::string strsettingpath = MyYaml::GetIns()->GetStringValue("method_setting");
    // 读取算法参数的内容
    getsetting(strsettingpath);
    // 获取动态检测参数的数量
    int nMethodCount = lstAction.size();
    // LogInfo << "TrainAnaEngine Init method list count:" << nMethodCount;
    if (nMethodCount > 2)
    {
        //计算变化值
        nRightComeFlagR3 = 0x00000001 << (lstAction.size() - 3);
        nRightComeFlagR2 = 0x00000001 << (lstAction.size() - 2) + nRightComeFlagR3;
        nRightComeFlagR1 = 0x00000001 << (lstAction.size() - 1) + nRightComeFlagR2;
        // LogInfo << "TrainAnaEngine Init nRightComeFlagR3:" << nRightComeFlagR3;
        // LogInfo << "TrainAnaEngine Init nRightComeFlagR2:" << nRightComeFlagR2;
        // LogInfo << "TrainAnaEngine Init nRightComeFlagR1:" << nRightComeFlagR1;
    }
    // 获取尾部车钩的偏移量
    nTailPixOffset = getTailPixOffset();
    // LogInfo << "TrainAnaEngine Init nTailPixOffset:" << nTailPixOffset;
    //初始化来车检测状态
    nStatus=TRAIN_INIT_STATUS;
    // 初始化引擎相关的参数
    vResetPartion();

    LogInfo << "TrainAnaEngine Init ok";
    return APP_ERR_OK;
}

APP_ERROR TrainAnaEngine::DeInit()
{
    LogInfo << "TrainAnaEngine DeInit ok";
    return APP_ERR_OK;
}


/**
* 参数初始化(列车结束时需调用)
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void TrainAnaEngine::InitParam()
{
    LogInfo << "TrainAnaEngine InitParam start";
    strTrainData_ = "";
    strTrainName_ = "";
    LogInfo << "TrainAnaEngine InitParam end";
}


/**
* 初始化车厢分割信息
* inParam : N/A
* outParam: N/A
* return  : N/A
*/
void TrainAnaEngine::vResetPartion()
{
    LogInfo << "TrainAnaEngine vResetPartion start";
	InitParam();
    // //行车状态为初始化
	// nStatus = TRAIN_INIT_STATUS;
    // nPreStatus = TRAIN_INIT_STATUS;

    //设定检测动态区域均为未发生变化
	nPicAreaChangeing = 0x00;
	nPrePicAreaChangeing = 0x00;
    //清空车厢划分列表
	lstPartInfo.clear();
    //初始化开始帧
    nLatestFrame = 0;

    // 根据帧率计算检测到车钩以后忽略检测的帧数
    // 1.默认车辆3.9秒通过
    // 2.默认车体长度 14.5(TRAIN_WIDTH)
    // 3.单位:米/秒
    fdefaultspeed = (TRAIN_WIDTH / 3.9);

    // 车体的三分之一作为忽略标准
    // 车体三分之一的通过时间
    float fpasstime = (TRAIN_WIDTH / 3.0) / fdefaultspeed;
    // 忽略帧数：时间*帧率
    nSamePartionIgnoreCount = fpasstime * nFrameRate;
    
    nPartionPassFrameCount = 0;

    LogInfo << "TrainAnaEngine vResetPartion start";
}

/**
* 格式化算分配置文件内容
* inParam : 单个算法配置分割后的字符信息
* outParam: 结构化的算法配置信息
* return  : N/A
*/
void TrainAnaEngine::vformatStructAnalyseInfo(std::vector<std::string> elems, AnalyseInfo &info)
{
    LogInfo << "TrainAnaEngine vformatStructAnalyseInfo start";
    //以下内容为文件读取的结果
    //算法类型 1动态检测 2车厢划分
    info.nType = -1;
    //检测敏感区域开始位置的X值
    info.nAreaX1 = -1;
    //检测敏感区域开始位置的Y值
    info.nAreaY1 = -1;
    //检测敏感区域结束位置的X值
    info.nAreaX2 = -1;
    //检测敏感区域结束位置的Y值
    info.nAreaY2 = -1;
    //算法结果等级相同做AND处理，不同做OR处理
    info.bOrLevel = -1;
    //是否以不同作为判断结果
    info.bDiff = false;
    //算法是否有效 1有效 0无效
    info.bOn = false;
    //判断相同的阈值
    info.dSameThresholdVal = 0.0;
    //判断不同的阈值
    info.dDiffThresholdVal = 0.0;
    //判断停车的累计帧数阈值
    info.nPauseMaxCoumt = 0;
    //图像增强的算法(位运算)
    //0x01 灰度化 
    //0x02 直方图均衡化
    //0x04 自定义
    info.npicimprovetype = -1;
    //OPENCV模板对比算法
    info.templemethod = -1;
    //直方图对比算法
    info.histmethod = -1;
    //自定义算法
    info.specmethod = -1;
    //图像比对的方法(位运算)
    //0x01 OPENCV模板对比
    //0x02 直方图对比
    //0x04 自定义
    info.compmethod = -1;
    //对比的基准图的路径
    info.baseImagePath = "";
    
    //以下内容为初始化结果
    //图像对比数值
    info.dComparePoint = 0.0;
    //上一帧对比数值
    info.dPreComparePoint = 0.0;
    //图像与基础状态是否发生变化
    info.bChanged = 0.0;
    //上一帧图像与基础状态是否发生变化
    info.bPreChanged = 0.0;
    //图像变化后(保持在一个平衡状态即值不变的状态)累计值
    info.nSameCount = 0;

    char *chrevptr;
    if (elems.size() == IMAGEANALYSE_NUM)
    {
        info.nType = atoi(elems[0].c_str());
        info.nAreaX1 = atoi(elems[1].c_str());
        info.nAreaY1 = atoi(elems[2].c_str());
        info.nAreaX2 = atoi(elems[3].c_str());
        info.nAreaY2 = atoi(elems[4].c_str());
        info.bOrLevel = atoi(elems[5].c_str());
        info.bDiff = (atoi(elems[6].c_str()) == 1) ? true : false;
        info.bOn = (atoi(elems[7].c_str()) == 1) ? true : false;
        info.dSameThresholdVal = strtod(elems[8].c_str(), &chrevptr);
        info.dDiffThresholdVal = strtod(elems[9].c_str(), &chrevptr);
        info.nPauseMaxCoumt = atoi(elems[10].c_str());
        info.npicimprovetype = atoi(elems[11].c_str());
        info.templemethod = atoi(elems[12].c_str());
        info.histmethod = atoi(elems[13].c_str());
        info.specmethod = atoi(elems[14].c_str());
        info.compmethod = atoi(elems[15].c_str());
        info.baseImagePath = elems[16];
    }
    LogInfo << "TrainAnaEngine vformatStructAnalyseInfo end";
}

/**
* 获取图像比对的结果值
* inParam : 算法配置信息
* inParam : 当前图像的Mat值
* inParam : 比对基础图像的Mat值
* outParam: N/A
* return  : 比对值的数组
*/
std::vector<double> TrainAnaEngine::getCompPoint(AnalyseInfo info, cv::Mat baseimg, cv::Mat tarpic)
{
    LogInfo << "TrainAnaEngine getCompPoint start";
    std::vector<double> dRetVal;
    cv::Point cvOutPoint;

    LogInfo << "TrainAnaEngine getCompPoint info.compmethod:" << info.compmethod;
    if ((info.compmethod & IMAGE_COMP_BY_SPEC) == IMAGE_COMP_BY_SPEC)
    {
        double dHistPoint = anapicbySpec(baseimg);
        dRetVal.push_back(dHistPoint);
    }
    //OPENCV模型比对方法
    if ((info.compmethod & IMAGE_COMP_BY_TEMPLE) == IMAGE_COMP_BY_TEMPLE)
    {
        double dtemplePoint = anapicbyTemple(baseimg, tarpic, info.templemethod, cvOutPoint);
        dRetVal.push_back(dtemplePoint);
    }
    //直方图比对方法
    if ((info.compmethod & IMAGE_COMP_BY_HIST) == IMAGE_COMP_BY_HIST)
    {
        double dHistPoint = anapicbyHist(baseimg, tarpic, info.histmethod);
        dRetVal.push_back(dHistPoint);
    }
    
    
    LogInfo << "TrainAnaEngine getCompPoint end:" << dRetVal[0];
    return dRetVal;
}

/**
* 获取算法配置文件的内容
* inParam : 算法配置文件路径
* outParam: N/A
* return  : N/A
*/
void TrainAnaEngine::getsetting(string strFilePath)
{
    //LogInfo << "TrainAnaEngine getsetting start";
    std::string strLineInfo;
    std::ifstream finfile(strFilePath);
    int nindex = 0;
    //按行读取
    while (std::getline(finfile, strLineInfo))
    {
        //LogInfo << "TrainAnaEngine getsetting nindex:" << nindex;
        //首行作为注释不读取
        if (nindex == 0)
        {
            nindex++;
            continue;
        }
        nindex++;
        std::vector<std::string> elems;
        //每行以逗号, 作为分隔符
        elems = MyUtils::getins()->split(strLineInfo, ",");

        AnalyseInfo strTempInfo;
        //根据逗号分割的列表数据 初始化算法配置信息
        vformatStructAnalyseInfo(elems, strTempInfo);
        //LogInfo << "TrainAnaEngine getsetting strTempInfo.nType :" << strTempInfo.nType ;
        //保存动态检测的算法到集合中
        if (strTempInfo.nType == TRAIN_ACTION)
        {
            lstAction.push_back(strTempInfo);
        }
        //保存车厢划分的算法到集合中
        else if(strTempInfo.nType == TRAIN_PARTION)
        {
            lstPartion.push_back(strTempInfo);
        }
    }
    //LogInfo << "TrainAnaEngine getsetting end";
}

/**
* 根据直方图做比对
* inParam : 当前图像的Mat值
* inParam : 比对基础图像的Mat值
* inParam : 比对算法
* outParam: N/A
* return  : 比对值
*/
double TrainAnaEngine::anapicbySpec(cv::Mat baseimg)
{
    LogInfo << "TrainAnaEngine anapicbySpec start";
    double match = 0.0;
    int nmean = 0;
    Mat tmp_m, tmp_sd;
    cv::Mat mtTempImage = mtresizeImage(baseimg, (int)(baseimg.cols / 4), (int)(baseimg.rows / 4));


    cv::Mat mtOutImage;
    cv::meanStdDev(mtTempImage, tmp_m, tmp_sd);
    nmean = (int)tmp_m.at<double>(0,0);
    int nwidth = mtTempImage.cols;
    int nHeight = mtTempImage.rows;
    std::vector<int> elems;
    int nmax = 0;
    int nmin = 0;
    for(int i = 0;i < nHeight;i++) {
        int ncheckval = 0;
        for(int j = 0; j < nwidth;j++) {
            ncheckval = ncheckval + mtTempImage.at<uchar>(i,j);
        }
        ncheckval = (int)(ncheckval / nwidth) - nmean;
        if(ncheckval > nmax){
            nmax = ncheckval;
        }
        if(ncheckval < nmin){
            nmin = ncheckval;
        }
        elems.push_back(ncheckval);
    }
    
    int nstartindex = 6;
    int nendindex = 24;
    int nblackcount = 0;
    for(int i = nstartindex + 1;i < nendindex;i++) {
        if(elems[i] < 0 && elems[i - 1] < 0){
            nblackcount++;
        } else {
            //nblackcount = 0;
        }
    }
    LogInfo << "TrainAnaEngine anapicbySpec nblackcount:" << nblackcount;
    if (nblackcount > ((nendindex - nstartindex) / 2)) {
        for(int i = 0;i < nHeight;i++) {
            elems[i] = 0 - elems[i];
        }
    }

    int nfirstpot = -1;
    int nsecondpot = -1;
    int nthirdpot = -1;
    int nfirstpotsum = 0;
    int nsecondpotsum = 0;
    int nthirdpotsum = 0;

    int npremax = 0;
    int npremin = 0;
    int nfirstmax = 0;
    int nfirstmin = 0;
    int nsecondmax = 0;
    int nsecondmin = 0;

    for(int i = 2; i < nHeight; i++) {
        LogInfo << "TrainAnaEngine elems[i] :" <<  elems[i];
        if (elems[i -2] < elems[i - 1] && elems[i - 1] < elems[i]  && nfirstpot == -1 && elems[i] > 0) {
            nfirstpot = i;
        }
        if (nfirstpot == -1) {
            if(npremax < elems[i]){
                npremax =  elems[i];
            }
            if(npremin > elems[i]){
                npremin =  elems[i];
            }
            continue;
        }
        if (elems[i -2] > elems[i - 1] && elems[i - 1] > elems[i]  && nsecondpot == -1 && elems[i] < 0) {
            nsecondpot = i;
        }
        if (nsecondpot == -1 && elems[i] > 0) {
            nfirstpotsum++;
            if(nfirstmax < elems[i]){
                nfirstmax =  elems[i];
            }
            if(nfirstmin > elems[i]){
                nfirstmin =  elems[i];
            }
        }
        if (nsecondpot == -1) {
            continue;
        }
        if (nthirdpot == -1 && elems[i] < 0) {
            nsecondpotsum++;
            if(nsecondmax < elems[i]){
                nsecondmax =  elems[i];
            }
            if(nsecondmin > elems[i]){
                nsecondmin =  elems[i];
            }
        }
        if (elems[i -2] < elems[i - 1] && elems[i - 1] < elems[i]  && nthirdpot == -1 && elems[i] > 0) {
            nthirdpot = i;
        }
        if (nthirdpot != -1) {
            nthirdpotsum++;
        }
    }

    LogInfo << "TrainAnaEngine nHeight :" << nHeight;
    LogInfo << "TrainAnaEngine nfirstpot :" << nfirstpot;
    LogInfo << "TrainAnaEngine nsecondpot :" << nsecondpot;
    LogInfo << "TrainAnaEngine nthirdpot :" << nthirdpot;
    LogInfo << "TrainAnaEngine nmax :" << nmax;
    LogInfo << "TrainAnaEngine nmin :" << nmin;
    LogInfo << "TrainAnaEngine npremin :" << npremin;
    LogInfo << "TrainAnaEngine npremax :" << npremax;
    LogInfo << "TrainAnaEngine nfirstmax :" << nfirstmax;
    LogInfo << "TrainAnaEngine nfirstmin :" << nfirstmin;
    LogInfo << "TrainAnaEngine nsecondmax :" << nsecondmax;
    LogInfo << "TrainAnaEngine nsecondmin :" << nsecondmin;
    if (
        (nfirstpot < (int)(nHeight / 3))
        && (nsecondpot > (int)(nHeight / 2)) && (nsecondpot < (int)(nHeight *2 / 3))
        && (nthirdpot > (int)(nHeight * 3/ 4))
        && (npremin < 0)
        && (nfirstmax > 0)
        && (nsecondmin < 0)
        //&& (nfirstpotsum > (nsecondpot - nfirstpot - 2))
        //&& (nsecondpotsum > (nthirdpot - nsecondpot - 2))
        //&& (nthirdpotsum >= (nHeight - nthirdpot - 1))
        //&& (nmax- nfirstmax > nmax/2)
        //&& (nsecondmin < nmin/2)
    ){
        match = 1.0;
        double firstsinoffset = 180.0/nfirstpot;
        double secondsinoffset = 180.0/(nsecondpot - nfirstpot);
        double thirdsinoffset = 180.0/(nthirdpot - nsecondpot);
        double dbaseoffset = 1.0 / (nHeight * 1.0);
        double dmaxoffset = (nmax-nmin) * 1.0 ;
        LogInfo << "TrainAnaEngine firstsinoffset :" << firstsinoffset;
        LogInfo << "TrainAnaEngine secondsinoffset :" << secondsinoffset;
        LogInfo << "TrainAnaEngine thirdsinoffset :" << thirdsinoffset;
        LogInfo << "TrainAnaEngine dbaseoffset :" << dbaseoffset;
        LogInfo << "TrainAnaEngine dmaxoffset :" << dmaxoffset;
        double dtmp = 0.0;
        for (int i = 0; i < nHeight; i++) {
            if (i < nfirstpot){
                dtmp = dbaseoffset * abs(elems[i] - sin((i)*firstsinoffset)*(npremin* 1.0))/ dmaxoffset;
	    } else if(i>= nfirstpot && i < nsecondpot){
                dtmp = dbaseoffset * abs(elems[i] - sin((i - nfirstpot)*secondsinoffset)*(nfirstmax * 1.0)) / dmaxoffset;
            } else if(i>= nsecondpot && i < nthirdpotsum){
                dtmp = dbaseoffset * abs(elems[i] - sin((i - nsecondpot)*thirdsinoffset)*(nsecondmin * 1.0)) / dmaxoffset;
            } else if(i >= nthirdpotsum){
                dtmp = 0.0;
            }
            LogInfo << "TrainAnaEngine befor match :" << match;
            LogInfo << "TrainAnaEngine dtmp :" << dtmp;
            match = match - dtmp;
            LogInfo << "TrainAnaEngine after match :" << match;
        }
        //match = 1.0;
    }
    if (strResultPath_for_test != "")
    {
        std::ostringstream ossfloat,ossint;
        ossfloat<<(int)(match * 1000);
        ossint<<ncurtime;
        std::string strscore(ossfloat.str());
        std::string strindex(ossint.str());
        std::string strtestpath = strResultPath_for_test + strindex + "_______" + strscore + ".jpg";
        LogInfo << "TrainAnaEngine anapicbySpec image to" << strtestpath;
        cv::imwrite(strtestpath, mtTempImage);
    
    }
    LogInfo << "TrainAnaEngine anapicbySpec end:" << match;
    return match;
}

/**
* 根据直方图做比对
* inParam : 当前图像的Mat值
* inParam : 比对基础图像的Mat值
* inParam : 比对算法
* outParam: N/A
* return  : 比对值
*/
double TrainAnaEngine::anapicbyHist(cv::Mat baseimg, cv::Mat tarpic, int method)
{
    LogInfo << "TrainAnaEngine anapicbyHist start";
    float hranges[] = {0, 180};
    float sranges[] = {0, 255};
    const float *ranges[] = {hranges, sranges};
    int hbins = 60, sbins = 64;
    int histSize[] = {hbins, sbins};
    int channels[] = {0};
    cv::Mat hist1, hist2;
    //获取直方图信息
    cv::calcHist(&baseimg, 1, channels, Mat(), hist1, 2, histSize, ranges);
    cv::calcHist(&tarpic, 1, channels, Mat(), hist2, 2, histSize, ranges);

    double match = cv::compareHist(hist1, hist2, method);
    LogInfo << "TrainAnaEngine anapicbyHist end:" << match;
    return match;
}

/**
* 根据模板做比对
* inParam : 当前图像的Mat值
* inParam : 比对基础图像的Mat值
* inParam : 比对算法
* outParam: 目标的坐标
* return  : 比对值
*/
double TrainAnaEngine::anapicbyTemple(cv::Mat baseimg, cv::Mat tarpic, int method, cv::Point &pPos)
{
    LogInfo << "TrainAnaEngine anapicbyTemple start";
    double dRetVal = 0.0;
    cv::Mat img_match;
    //LogInfo << "TrainAnaEngine anapicbyTemple baseimg.cols:" << baseimg.cols;
    //LogInfo << "TrainAnaEngine anapicbyTemple baseimg.rows:" << baseimg.rows;
    //LogInfo << "TrainAnaEngine anapicbyTemple tarpic.cols:" << tarpic.cols;
    //LogInfo << "TrainAnaEngine anapicbyTemple tarpic.rows:" << tarpic.rows;
    
    cv::matchTemplate(baseimg, tarpic, img_match, method);

    double minval, maxval;
    cv::Point minloc, maxloc;
    cv::minMaxLoc(img_match, &minval, &maxval, &minloc, &maxloc);

    //LogInfo << "TrainAnaEngine anapicbyTemple method:" << method;
    //TM_SQDIFF算法和TM_SQDIFF_NORMED算法最小值为最匹配的数值
    if (
        method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED)
    {
        dRetVal = minval;
        pPos = minloc;
    }
    //TM_CCORR TM_CCORR_NORMED TM_CCOEFF TM_CCOEFF_NORMED算法最大值为最匹配的数值
    else if (
        method == cv::TM_CCORR || method == cv::TM_CCORR_NORMED || method == cv::TM_CCOEFF || method == cv::TM_CCOEFF_NORMED)
    {
        dRetVal = maxval;
        pPos = maxloc;
    }
    //LogInfo << "TrainAnaEngine anapicbyTemple end" << dRetVal;
    //if (strResultPath_for_test != "" && nLatestFrame != 0)
    if(false)
    {
        cv::Mat cvdes = Mat(baseimg.rows, baseimg.cols + tarpic.cols, baseimg.type());
        cv::Mat cvleft = cvdes(cv::Rect(0, 0, baseimg.cols, baseimg.rows));
        baseimg.copyTo(cvleft);
        cv::Mat cvright = cvdes(cv::Rect(baseimg.cols, 0, tarpic.cols, tarpic.rows));
        tarpic.copyTo(cvright);

        std::ostringstream ossfloat,ossint;
        ossfloat<<(int)(dRetVal*1000);
        ossint<<nLatestFrame;
        std::string strscore(ossfloat.str());
        std::string strindex(ossint.str());
        std::string strtestpath = strResultPath_for_test + strindex + "_" + strscore + ".jpg";
        //LogInfo << "TrainAnaEngine anapicbyTemple image to" << strtestpath;
        cv::imwrite(strtestpath, cvdes);
    
    }
    return dRetVal;
}

/**
* 读取图像文件并转化为Mat值
* inParam : 读取文件的路径
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtdecodeImageDatabyFile(string path)
{
    LogInfo << "TrainAnaEngine mtdecodeImageDatabyFile start";
    //LogInfo << "TrainAnaEngine mtdecodeImageDatabyFile path:" << path;
    std::ifstream imagefile(path, std::ios::binary);
    std::vector<char> imagedata;
    imagefile >> std::noskipws;
    std::copy(std::istream_iterator<char>(imagefile), std::istream_iterator<char>(), std::back_inserter(imagedata));
    cv::Mat jpegimage = cv::imdecode(Mat(imagedata), cv::IMREAD_COLOR);
    imagefile.close();
    LogInfo << "TrainAnaEngine mtdecodeImageDatabyFile end";
    return jpegimage;
}


/**
* 将图像二进制数据转化为Mat值
* inParam : 二进制数据指针
* inParam : 二进制数据长度
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtdecodeImageDatabyBin(LPVOID lpimgdata, int imglen)
{
    LogInfo << "TrainAnaEngine mtdecodeImageDatabyBin start";
    //LogInfo << "TrainAnaEngine mtdecodeImageDatabyBin imglen:" << imglen;
    const char *buffer = static_cast<char *>(lpimgdata);
    vector<uchar>::size_type size = imglen;

    std::vector<unsigned char> imagedata(buffer, buffer + size);
    cv::Mat jpegimage = imdecode(imagedata, cv::IMREAD_COLOR);
    LogInfo << "TrainAnaEngine mtdecodeImageDatabyBin end";
    return jpegimage;
}


/**
* 图像增强
* inParam : 原始图片的Mat值
* inParam : 算法配置信息
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtImproveImage(cv::Mat inImage, AnalyseInfo info, bool bcvread)
{
    LogInfo << "TrainAnaEngine mtImproveImage start";
    cv::Mat mtOutImage = inImage;
    //cv::Mat mtOutImage;
    int x1 = info.nAreaX1;
    int y1 = info.nAreaY1;
    int width = info.nAreaX2 - info.nAreaX1;
    int height = info.nAreaY2 - info.nAreaY1;
    int type = info.nType;
    int mode = (bcvread == false) ? cv::COLOR_YUV2GRAY_420 : cv::COLOR_RGB2GRAY;

    //LogInfo << "TrainAnaEngine mtImproveImage x1:"<<x1;
    //LogInfo << "TrainAnaEngine mtImproveImage y1:"<<y1;
    //LogInfo << "TrainAnaEngine mtImproveImage width:"<<width;
    //LogInfo << "TrainAnaEngine mtImproveImage height:"<<height;
    //LogInfo << "TrainAnaEngine mtImproveImage type:"<<type;
    
    //图像灰度处理
    if (((info.npicimprovetype & IMAGE_GRAY) == IMAGE_GRAY) && (mode != -1))
    {
        mtOutImage = mtgrayImage(mtOutImage, mode);
    }
    //图像直方图均衡化处理
    if ((info.npicimprovetype & IMAGE_EQUALIZE) == IMAGE_EQUALIZE)
    {
        mtOutImage = mtequalizeImage(mtOutImage);
    }
    //图像进行gamma变换
    if ((info.npicimprovetype & IMAGE_GAMMA) == IMAGE_GAMMA)
    {
        mtOutImage = mtGammaImage(mtOutImage, 1.0);
    }
    //自定义处理
    if ((info.npicimprovetype & IMAGE_SPEC) == IMAGE_SPEC)
    {
        mtOutImage = mtspecImage(mtOutImage);
    }
    LogInfo << "TrainAnaEngine mtImproveImage end";
    return mtOutImage;
}

/**
* 修改图像尺寸
* inParam : 原始图片的Mat值
* inParam : 目标宽度
* inParam : 目标高度
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtresizeImage(cv::Mat inImage, int width, int height)
{
    LogInfo << "TrainAnaEngine mtresizeImage start";
    //LogInfo << "TrainAnaEngine mtresizeImage width:"<<width;
    //LogInfo << "TrainAnaEngine mtresizeImage height:"<<height;
    cv::Mat mtTempImage = inImage;
    cv::Mat mtOutImage;

    cv::resize(mtTempImage, mtOutImage, cv::Size(width, height));
    LogInfo << "TrainAnaEngine mtresizeImage end";
    return mtOutImage;
}

/**
* 裁剪图像
* inParam : 原始图片的Mat值
* inParam : 开始位置的X值
* inParam : 开始位置的Y值
* inParam : 结束位置的X值
* inParam : 结束位置的Y值
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtareaImage(cv::Mat inImage, int x1, int y1, int x2, int y2)
{
    LogInfo << "TrainAnaEngine mtareaImage start";
    //LogInfo << "TrainAnaEngine mtareaImage x1:"<<x1;
    //LogInfo << "TrainAnaEngine mtareaImage y1:"<<y1;
    //LogInfo << "TrainAnaEngine mtareaImage x2:"<<x2;
    //LogInfo << "TrainAnaEngine mtareaImage y2:"<<y2;
    cv::Mat mtTempImage = inImage;
    
    cv::Rect recArea = cv::Rect(x1, y1, abs(x2 - x1), abs(y2 - y1));
    cv::Mat mtOutImage = mtTempImage(recArea);

    //LogInfo << "TrainAnaEngine mtareaImage out cols:"<< mtOutImage.cols;
    //LogInfo << "TrainAnaEngine mtareaImage out rows:"<< mtOutImage.rows;
    LogInfo << "TrainAnaEngine mtareaImage end";
    return mtOutImage;
}

/**
* 图像灰度化
* inParam : 原始图片的Mat值
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtgrayImage(cv::Mat inImage, int mode)
{
    LogInfo << "TrainAnaEngine mtgrayImage start";
    cv::Mat mtTempImage = inImage;
    cv::Mat mtOutImage;
    cv::cvtColor(mtTempImage, mtOutImage, mode);
    LogInfo << "TrainAnaEngine mtgrayImage end";
    return mtOutImage;
}

/**
* 图像直方图均衡化
* inParam : 原始图片的Mat值
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtequalizeImage(cv::Mat inImage)
{
    LogInfo << "TrainAnaEngine mtequalizeImage start";
    cv::Mat mtTempImage = inImage;
    cv::Mat mtOutImage;

    cv::equalizeHist(mtTempImage, mtOutImage);
    LogInfo << "TrainAnaEngine mtequalizeImage end";
    return mtOutImage;
}
/**
* 图像进行gamma变换
* inParam : 原始图片的Mat值
* inParam : gamma值
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtGammaImage(cv::Mat inImage, float fgamma = 1.0)
{
    LogInfo << "TrainAnaEngine mtGammaImage start";
    //LogInfo << "TrainAnaEngine mtGammaImage fgamma:"<<fgamma;
    cv::Mat mtTempImage = inImage;
    cv::Mat mtOutImage;

    for (int i = 0; i < mtTempImage.rows; i++){
        for (int j = 0; j < mtTempImage.cols; j++) {
            mtTempImage.at<uchar>(i, j) = 6*pow((double)inImage.at<unsigned char>(i, j), fgamma);
        }
    }
    cv::normalize(mtTempImage, mtTempImage, 0, 255, NORM_MINMAX);
    cv::convertScaleAbs(mtTempImage, mtOutImage);
    LogInfo << "TrainAnaEngine mtGammaImage end";
    return mtOutImage;
}


/**
* 图像进行自定义处理
* inParam : 原始图片的Mat值
* outParam: N/A
* return  : 图像Mat值
*/
cv::Mat TrainAnaEngine::mtspecImage(cv::Mat inImage)
{
    LogInfo << "TrainAnaEngine mtspecImage start";
    cv::Mat mtTempImage = inImage;
    cv::Mat mtOutImage;
    //中值滤波，降采样
    cv::medianBlur(inImage,mtTempImage,3);
    cv::resize(mtTempImage, mtOutImage, Size((int)(mtTempImage.rows*0.5),(int)(mtTempImage.cols*0.5) ), INTER_LINEAR);
    LogInfo << "TrainAnaEngine mtspecImage end";
    return mtOutImage;
}

/*
* 获取最近100帧的dComparePoint的最大值与最小值之差
* inParam : 第i个方框的dComparePoint值
* inParam : 第i个框的索引
* outParam: N/A
* return  : N/A
*/
float TrainAnaEngine::GetPointMaxReduceMin(float dComparePoint,int index){
    static float  array[20] ={0.0};
    switch (index)
    {
    case 0:
    static int num_count1=0;
    static float  array1[20] ={0.0};
    array1[num_count1]=dComparePoint;
    num_count1++;
    if (num_count1>19){
        num_count1=0;
    }
    for(int i=0;i<=19;i++){
        array[i]=array1[i];
    }
    break;


    case 1:
    static int num_count2=0;
    static float  array2[20] ={0.0};
    array2[num_count2]=dComparePoint;
    num_count2++;
    if (num_count2>19){
        num_count2=0;
    }
    for(int i=0;i<=19;i++){
        array[i]=array2[i];
    }
    break;


    case 2:
    static int num_count3=0;
    static float  array3[20] ={0.0};
    array3[num_count3]=dComparePoint;
    num_count3++;
    if (num_count3>19){
        num_count3=0;
    }
    for(int i=0;i<=19;i++){
        array[i]=array3[i];
    }
    break;
    }

    sort(array,array+20);
    return (array[19]-array[0]);
}
/**
* 来车动态检测
* inParam : 原始图片的Mat值
* inParam : 原始图片的时间戳
* outParam: N/A
* return  : N/A
*/
void TrainAnaEngine::checkAction(cv::Mat baseimg, uint64_t i64TimeStamp)
{
    #ifdef SHOW_RECTANGLE
    cv::Mat img=baseimg.clone(); 
    cv::Scalar sca(0,0,255);
    #endif


    //count[5]记录选择区域的状态为来车所保持的帧数，当帧数大于5时，改变选择区域的状态为来车
    static int count[5]={0};
    LogInfo << "TrainAnaEngine checkAction start";
    // 记录上一帧的行车状态
    nPreStatus = nStatus;
    // 记录上一帧的动态变化情况
    nPrePicAreaChangeing = nPicAreaChangeing;
    // 初始化当前帧的动态变化情况
    nPicAreaChangeing = 0x00;
    // 检测区域数量
    int nlistlen = lstAction.size();
    //LogInfo << "TrainAnaEngine checkAction nlistlen:" << nlistlen;
    //LogInfo << "TrainAnaEngine checkAction i64TimeStamp:" << i64TimeStamp;

    
    for (int i = 0; i < nlistlen; i++)
    {
        // 根据检测区域切分当前帧
        cv::Mat cvImg1 = mtareaImage(baseimg, lstAction[i].nAreaX1, lstAction[i].nAreaY1, lstAction[i].nAreaX2, lstAction[i].nAreaY2);
        cvImg1 = mtImproveImage(cvImg1, lstAction[i], false);
        // 根据检测区域切分基础帧
        cv::Mat cvImg2 = mtareaImage(cvFirstImg, lstAction[i].nAreaX1+5, lstAction[i].nAreaY1+5, lstAction[i].nAreaX2-5, lstAction[i].nAreaY2-5);
        cvImg2 = mtImproveImage(cvImg2, lstAction[i], false);
        // 获取比对数值
        std::vector<double> lstRet = getCompPoint(lstAction[i], cvImg1, cvImg2);
        // 记录上一帧比对数值
        lstAction[i].dPreComparePoint = lstAction[i].dComparePoint;
        // 记录当前帧比对数值
        lstAction[i].dComparePoint = lstRet[0];
        //LogInfo << "TrainAnaEngine checkAction i:" << i;
        //LogInfo << "TrainAnaEngine checkAction nlistlen:" << nlistlen;
        //LogInfo << "TrainAnaEngine checkAction nStatus:" << nStatus;
        //LogInfo << "TrainAnaEngine checkAction lstAction[i].dComparePoint:" << lstAction[i].dComparePoint;
        //LogInfo << "TrainAnaEngine checkAction lstAction[i].dSameThresholdVal:" << lstAction[i].dSameThresholdVal;

        #ifdef SHOW_RECTANGLE
        //方框的位置
        cv::Rect rect(lstAction[i].nAreaX1,lstAction[i].nAreaY1,(lstAction[i].nAreaX2-lstAction[i].nAreaX1),(lstAction[i].nAreaY2-lstAction[i].nAreaY1));
        //point1当前的方框状态显示的位置
        cv::Point point(lstAction[i].nAreaX1,lstAction[i].nAreaY1-90);
        //point2时当前的方框相似度的值显示的位置
        cv::Point point2(lstAction[i].nAreaX1,lstAction[i].nAreaY1-60);
        //point2时当前的方框累加的值显示的位置
        cv::Point point3(lstAction[i].nAreaX1,lstAction[i].nAreaY1-30);
        cv::rectangle(img,rect,sca,3); 
        #endif
        
        // 比对数值小于设定的阈值则判断检测区域发生变化
         if (lstAction[i].dComparePoint < lstAction[i].dSameThresholdVal){
            //LogInfo << "TrainAnaEngine checkAction start nPicAreaChangeing:" << nPicAreaChangeing;
            // 设定状态 当前区域存在变化
            count[i]++;
            if(count[i]>=5){    
                // nPicAreaChangeing += 0x00000001 << i;
                lstAction[i].bChanged = true;
                if(count[i]>10)
                count[i]=10;
            }
            //LogInfo << "TrainAnaEngine checkAction end nPicAreaChangeing:" << nPicAreaChangeing;
        }else{
            count[i]--;
            if(count[i]<=0){
                count[i]=0;
                //nPicAreaChangeing =(nPicAreaChangeing&(~(0x00000001<<i)));
                lstAction[i].bChanged = false;
            }       
        }
 
        #ifdef SHOW_RECTANGLE
        if( lstAction[i].bChanged){
            cv::putText(img,"yes",point,5,1.5,sca,3);
            LogInfo<<"current img id   "<<num << "  yes";
        }else{
            cv::putText(img,"no",point,5,1.5,sca,3);
            LogInfo<<"current img id   "<<num << "  no";
        } 
        cv::putText(img,"similarity: "+to_string((int)((lstAction[i].dComparePoint)*1000)),point2,5,1.5,sca,3);
         LogInfo<<"current img id   "<<num << "  similarity:  "<<(int)((lstAction[i].dComparePoint)*1000);
        #endif
        


        // 如果最近100帧的最大值与最小值小于0.03视为无变化
        if (GetPointMaxReduceMin(lstAction[i].dComparePoint,i)< 0.03){
            lstAction[i].fluctuationFlag=true;
        }else{
            lstAction[i].fluctuationFlag=false;
        }

        #ifdef SHOW_RECTANGLE
        if(lstAction[i].fluctuationFlag){
            cv::putText(img,"fluctuation Low",point3,5,1.5,sca,3);
            LogInfo<<"current img id   "<<num << "  fluctuation Low";
        }else{
            cv::putText(img,"fluctuation High",point3,5,1.5,sca,3);
            LogInfo<<"current img id   "<<num << "  fluctuation High";
        }
        #endif

        lstAction[i].bPreChanged = lstAction[i].bChanged;
    }
    
    //LogInfo << "TrainAnaEngine checkAction nStatus:" << nStatus;
    //LogInfo << "TrainAnaEngine checkAction nPicAreaChangeing:" << nPicAreaChangeing;
    //LogInfo << "TrainAnaEngine checkAction nPrePicAreaChangeing:" << nPrePicAreaChangeing;





    /************************************************状态切换****************************************************************/
    // 状态无车（并进行基础帧替换） -> 检测有车
    if (nStatus == TRAIN_INIT_STATUS){
        //LogInfo << "TrainAnaEngine checkAction TRAIN_INIT_STATUS start";
        // 动态检测区域状态是否发生变化 true 发生 false 未发生
        bool bStatuschanged = false;
        //根据方框的编号以及对应的状态判断当前来车的方向以及有无来车
        //0对应左侧方框一，1对应中间方框
        if((lstAction[0].bChanged&&lstAction[1].bChanged&&(!lstAction[2].bChanged))||(lstAction[0].bChanged&(!lstAction[1].bChanged)&lstAction[2].bChanged)){
            nStatus = TRAIN_RIGHT_RUN;
            bStatuschanged = true;
        }else if(lstAction[1].bChanged&&lstAction[2].bChanged&&(!lstAction[0].bChanged)){
            nStatus = TRAIN_LEFT_RUN;
            bStatuschanged = true;
        }
        // }else if(lstAction[0].bChanged&&lstAction[1].bChanged&&lstAction[2].bChanged){
        //     nStatus = TRAIN_RIGHT_RUN;
        //     bStatuschanged = true;
        // }

        // 状态发生变化
        if (bStatuschanged == true) {
            // 开始记录车厢划分信息/添加一节车厢划分信息
            PartionInfo stTempInfo;
            stTempInfo.nindex = 1;
            stTempInfo.startframe = 1;
            stTempInfo.i64StartTimeStamp = i64TimeStamp;
            lstPartInfo.push_back(stTempInfo);            
        } 

         //基础帧替换的第一种情况。
        //用于计算高校相似度保持的帧数，当帧数保持10帧以上则进行帧替换
        static int count_num=0;
        bool bEmpty1=true;
        for (int i = 0; i < nlistlen; i++) {
           bEmpty1=bEmpty1&&(lstAction[i].dPreComparePoint>=0.95);
        }
        if (bEmpty1){
            count_num++;
            if(count_num>=10) {
                // 没有发生变化，则将该帧作为基础帧       
                cvFirstImg = baseimg;

                count_num=0;           
            }
        }else{
            count_num=0;
        }


        //基础帧替换的第二种情况,过车后引起的相似度下降，需要最近100帧相似度波动小于0.03
        bool bEmpty2=true;
        static int count_num2=0;
        for (int i = 0; i < nlistlen; i++) {
           bEmpty2=bEmpty2&&(!lstAction[i].bChanged);
           bEmpty2=bEmpty2&&(lstAction[i].fluctuationFlag);
        }
        if(bEmpty2){
             count_num2++;
             if(count_num2>100){
                cvFirstImg = baseimg;
                count_num2=0;
             }          
        }else{
            count_num2=0;
        }

        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  train no";
        cv::putText(img,"train no",cv::Point(150,200),5,4,sca,3);
        #endif 

    }else if(nStatus == TRAIN_LEFT_RUN){
        bool bpaused = false;
        bool bpassed = false;
        bool noFluctuationFlag = true;
        unsigned short int bpaused_count = 0;


        //0,1,2全部无改变，相似度较高，视为车辆已经过去
        //0改变，1，2无变化仍视为车辆已经过去
        if(
        (!lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(!lstAction[2].bChanged)||
        (lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(!lstAction[2].bChanged)){ 
            bpassed=true;    

        }

        for (int i = 0; i < nlistlen; i++)
        {
            //任意两个及以上改变视为停车
            if(lstAction[i].bChanged){
                bpaused_count++;
            }
            noFluctuationFlag = (noFluctuationFlag && lstAction[i].fluctuationFlag);
        }

        bpaused=((bpaused_count>=2 )&& noFluctuationFlag);
        bpassed=(bpassed && noFluctuationFlag);

         //计算累计帧数
        static unsigned short int count1=0;
        static unsigned short int count2=0;
           // 状态 有车 -> 停车
        if (bpaused == true)
        {
            count1++;
            if(count1>50){
                nStatus = TRAIN_PAUSE;
                count1=0;
                count2=0;
            }  
        }else{
            count1=0;
        }
        // 状态 有车 -> 完全通过
        if (bpassed == true)
        {
            count2++;
            if(count2>50){
                nStatus = TRAIN_PASSED;
                count1=0;
                count2=0;
            } 
        }else{
            count2=0;
            
        }

        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  director left ";
        cv::putText(img,"director left",cv::Point(150,200),5,4,sca,3);    
        #endif

    }else if (nStatus == TRAIN_RIGHT_RUN){
        bool bpaused = false;
        bool bpassed = false;
        bool noFluctuationFlag = true;
        unsigned short int bpaused_count = 0;

        //0,1,2全部无改变，相似度较高，视为车辆已经过去
        //2改变，0,1无变化仍视为车辆已经过去
        if(
        (!lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(!lstAction[2].bChanged)||
        (!lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(lstAction[2].bChanged)){ 
            bpassed=true;    
        }

        for (int i = 0; i < nlistlen; i++)
        {
            //任意两个及以上改变视为停车
            if(lstAction[i].bChanged){
                bpaused_count++;
            }
            noFluctuationFlag = (noFluctuationFlag && lstAction[i].fluctuationFlag);
        }

        bpaused=((bpaused_count>=2 )&& noFluctuationFlag);
        bpassed=(bpassed && noFluctuationFlag);

         //计算累计帧数
        static unsigned short int count1=0;
        static unsigned short int count2=0;
          // 状态 有车 -> 停车
        if (bpaused == true)
        {
            count1++;
            if(count1>50){
                nStatus = TRAIN_PAUSE;
                count1=0;
                count2=0;
            }  
        }else{
            count1=0;
        }
        // 状态 有车 -> 完全通过
        if (bpassed == true)
        {
            count2++;
            if(count2>50){
                nStatus = TRAIN_PASSED;
                count1=0;
                count2=0;
            } 
        }else{
            count2=0;
        }
        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  director right ";
        cv::putText(img,"director right",cv::Point(150,200),5,4,sca,3);    
        #endif
    }else if (nStatus == TRAIN_UNKOWN_RUN){

        bool bpaused = false;
        bool bpassed = false;
        bool noFluctuationFlag = true;
        unsigned short int bpaused_count = 0;
        //0,1,2全部无改变，相似度较高，视为车辆已经过去
        //2改变，0,1无变化仍视为车辆已经过去,或者0给改变，12无变化
        if(
        (!lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(!lstAction[2].bChanged)||
        (!lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(lstAction[2].bChanged)||
        (lstAction[0].bChanged)&&(!lstAction[1].bChanged)&&(!lstAction[2].bChanged)
        ){ 
            bpassed=true;    
        }

        for (int i = 0; i < nlistlen; i++)
        {
            //任意两个及以上改变视为停车
            if(lstAction[i].bChanged){
                bpaused_count++;
            }
            noFluctuationFlag = (noFluctuationFlag && lstAction[i].fluctuationFlag);
        }

        bpaused=((bpaused_count>=2 )&& noFluctuationFlag);
        bpassed=(bpassed && noFluctuationFlag);

        //计算累计帧数
        static unsigned short int count1=0;
        static unsigned short int count2=0;
        // 状态 有车 -> 停车
        if (bpaused == true)
        {
            count1++;
            if(count1>50){
                nStatus = TRAIN_PAUSE;
                count1=0;
                count2=0;
            }  
        }else{
              count1=0;
        }
        // 状态 有车 -> 完全通过
        if (bpassed == true)
        {
            count2++;
            if(count2>50){
                nStatus = TRAIN_PASSED;
                count1=0;
                count2=0;
            } 
        }else{
                count2=0;
        }

        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  director unkonw ";
        cv::putText(img,"director unkonw",cv::Point(150,200),5,4,sca,3);    
        #endif
    }else if(nStatus == TRAIN_PAUSE){
        static short int count=0; 
        //停车状态只检测波动，三块全部波动保持20帧即重启
        if(!lstAction[0].fluctuationFlag&&!lstAction[1].fluctuationFlag&&!lstAction[2].fluctuationFlag){
            if(count>20){
              nStatus=TRAIN_RESTART;
              count=0;
            }
            count++;
        }
        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  train stop ";
        cv::putText(img,"train stop",cv::Point(150,200),5,4,sca,3);
        #endif 
    }else if(nStatus == TRAIN_RESTART){
        //切换到TRAIN_UNKOWN_RUN
        nStatus=TRAIN_UNKOWN_RUN;
        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  train restort ";
        cv::putText(img,"train restort",cv::Point(150,200),5,4,sca,3);
        #endif 
    }else if(nStatus == TRAIN_PASSED){
        //切换回TRAIN_INIT_STATUS状态
        nStatus = TRAIN_INIT_STATUS;
        #ifdef SHOW_RECTANGLE
        LogInfo<<"current img id   "<<num << "  train passed ";
        cv::putText(img,"train passed",cv::Point(150,200),5,4,sca,3);
        #endif 
    }

    if(nStatus!=TRAIN_INIT_STATUS){
        #ifdef SHOW_RECTANGLE
        if(num%4==0){
            LogInfo<<"current img id   "<<num << "  state:  "<<nStatus;
            cv::putText(img,"state: "+to_string(nStatus),cv::Point(150,400),5,4,sca,5);  
            imwrite(to_string(num)+".jpg",img);
        }
        #endif 
    }

    LogInfo << "TrainAnaEngine checkAction end";
}

/**
* 车厢划分检测
* inParam : 原始图片的Mat值
* inParam : 原始图片的时间戳
* outParam: N/A
* return  : N/A
*/
bool TrainAnaEngine::checkPartion(cv::Mat baseimg, uint64_t i64TimeStamp)
{
    LogInfo << "TrainAnaEngine checkPartion start";
    // 初始化是否有划分信息
    bool bPartion = false;
    // 记录上一帧的判断等级(相同作and处理 不同作or处理)
    int nPreOrLevel = 1;
   // LogInfo << "TrainAnaEngine checkPartion nStatus:" << nStatus;
    // 停车状态不进行划分信息检测
    //if(false){
    if ((nStatus == TRAIN_PAUSE) 
        || (nStatus == TRAIN_INIT_STATUS)
        || (nStatus == TRAIN_PASSED)){    
        return bPartion;
    }
    //LogInfo << "TrainAnaEngine checkPartion lstPartion.size():" << lstPartion.size();
    // 根据判断算法的数量进行循环比对
    bPartion = true;
    for (int i = 0; i < lstPartion.size(); i++)
    {
        //LogInfo << "TrainAnaEngine checkPartion i:" << i;
        // 算法不开启则跳过
        if (lstPartion[i].bOn == false)
        {
            //LogInfo << "TrainAnaEngine checkPartion lstPartion[i].bOn  i:" << i;
            continue;
        }

        // 对当前帧图片进行切割
        cv::Mat baseimgin = mtareaImage(baseimg, lstPartion[i].nAreaX1, lstPartion[i].nAreaY1, lstPartion[i].nAreaX2, lstPartion[i].nAreaY2);
        // 对当前帧图片进行画面增强
        //LogInfo << "TrainAnaEngine checkPartion cvImg1 cols:" << baseimgin.cols;
        //LogInfo << "TrainAnaEngine checkPartion cvImg1 rows:" << baseimgin.rows;
        cv::Mat cvImg1 = mtImproveImage(baseimgin, lstPartion[i], false);
        //LogInfo << "TrainAnaEngine checkPartion cvImg1 cols:" << cvImg1.cols;
        //LogInfo << "TrainAnaEngine checkPartion cvImg1 rows:" << cvImg1.rows;
        cv::Mat cvImg2;
        //LogInfo << "TrainAnaEngine checkPartion lstPartion[i].baseImagePath:" << lstPartion[i].baseImagePath;
        if (lstPartion[i].baseImagePath == "None")
        {   
            cv::Mat cvFirstImgin = mtareaImage(cvFirstImg, lstPartion[i].nAreaX1 - 10, lstPartion[i].nAreaY1 -10, lstPartion[i].nAreaX2 -10, lstPartion[i].nAreaY2 -10);
            // 设定为None 用基础图像进行比对
            cvImg2 = mtImproveImage(cvFirstImgin, lstPartion[i], false);
        }
        else
        {
            // 设定有路径则读取路径的文件进行比对
            cvImg2 = cv::imread(lstPartion[i].baseImagePath, cv::IMREAD_GRAYSCALE);
            // cvImg2 = mtdecodeImageDatabyFile(lstPartion[i].baseImagePath);
            // 图像增强
            cvImg2 = mtImproveImage(cvImg2, lstPartion[i], true);
        }
        // 获取比对值
        //LogInfo << "TrainAnaEngine checkPartion cvImg1 cols:" << cvImg1.cols;
        //LogInfo << "TrainAnaEngine checkPartion cvImg1 rows:" << cvImg2.rows;

        std::vector<double> lstRet = getCompPoint(lstPartion[i], cvImg1, cvImg2);
        lstPartion[i].dComparePoint = lstRet[0];
        //LogInfo << "TrainAnaEngine checkPartion lstPartion[i].dComparePoint:" << lstPartion[i].dComparePoint;

        // 获取是相同还是不同作为判断标准
        bool bCurrentRet = (lstPartion[i].bDiff == true) ? lstPartion[i].dComparePoint < lstPartion[i].dDiffThresholdVal : lstPartion[i].dComparePoint > lstPartion[i].dSameThresholdVal;
        // 对比等级不同 进行判断结果OR处理
        if (lstPartion[i].bOrLevel != nPreOrLevel)
        {
            //LogInfo << "TrainAnaEngine checkPartion lstPartion[i].bOrLevel != nPreOrLevel";
            bPartion = bPartion || bCurrentRet;
        }
        // 比对等级相同 进行判断结果AND处理
        else
        {
            //LogInfo << "TrainAnaEngine checkPartion lstPartion[i].bOrLevel == nPreOrLevel";
            bPartion = bPartion && bCurrentRet;
        }
        // 判断结果存在false的情况 直接停止判断确定没有车厢划分信息
        if (bPartion == false)
        {
            //LogInfo << "TrainAnaEngine checkPartion bPartion == false";
            break;
        }
    }
    // 存在车厢划分信息
    if (bPartion == true)
    {
        //LogInfo << "TrainAnaEngine checkPartion bPartion == true for end";
        // nPartionPassFrameCount = 0;
        // 获取最新的车厢节数
        int nPrePartionIndex = lstPartInfo.size() - 1;
        
        // 获取时间戳
        lstPartInfo[nPrePartionIndex].i64EndTimeStamp = i64TimeStamp;
        // 根据开始帧时间戳和结束帧时间错 计算当节车厢的行车速度
        //LogInfo << "TrainAnaEngine checkPartion bPartion == true lstPartInfo[nPrePartionIndex].ftime：" << abs(lstPartInfo[nPrePartionIndex].i64EndTimeStamp - lstPartInfo[nPrePartionIndex].i64StartTimeStamp);
        // 根据时间戳计算时间差
        float nTimePassed = (abs(lstPartInfo[nPrePartionIndex].i64EndTimeStamp - lstPartInfo[nPrePartionIndex].i64StartTimeStamp)) * 1.0;
        lstPartInfo[nPrePartionIndex].fspeed = (TRAIN_WIDTH * 1000.0) /nTimePassed;
        // 
        //nSamePartionIgnoreCount = (nTimePassed / (3 * 5000)) * nFrameRate;
        // 结束帧为当前帧再往后 (除以2的原因：中间为车钩，车钩后的车体宽度为整个镜头的宽度除以2)
        lstPartInfo[nPrePartionIndex].endframe = nLatestFrame + getOffsetFrame(lstPartInfo[nPrePartionIndex].fspeed, (TRAIN_IN_CAMERA_WIDTH / 2), nFrameRate);
        //LogInfo << "TrainAnaEngine checkPartion bPartion == true lstPartInfo[nPrePartionIndex].endframe" << lstPartInfo[nPrePartionIndex].endframe;
        lstPartInfo[nPrePartionIndex].bfuncconfirmed = true;
        //LogInfo << "TrainAnaEngine checkPartion bPartion == true lstPartInfo[nPrePartionIndex].i64EndTimeStamp：" << lstPartInfo[nPrePartionIndex].i64EndTimeStamp;
        //LogInfo << "TrainAnaEngine checkPartion bPartion == true lstPartInfo[nPrePartionIndex].i64StartTimeStamp:" << lstPartInfo[nPrePartionIndex].i64StartTimeStamp;
        // 如果时间戳相同则不是划分信息
        if (lstPartInfo[nPrePartionIndex].i64EndTimeStamp == lstPartInfo[nPrePartionIndex].i64StartTimeStamp) {
            bPartion = false;
            return bPartion;
        }

        
        //LogInfo << "TrainAnaEngine checkPartion nPrePartionIndex" << nPrePartionIndex;
        //LogInfo << "TrainAnaEngine checkPartion lstPartInfo[nPrePartionIndex].i64EndTimeStamp" << lstPartInfo[nPrePartionIndex].i64EndTimeStamp;
        //LogInfo << "TrainAnaEngine checkPartion lstPartInfo[nPrePartionIndex].nLatestFrame" << nLatestFrame;
        //LogInfo << "TrainAnaEngine checkPartion lstPartInfo[nPrePartionIndex].fspeed:" << lstPartInfo[nPrePartionIndex].fspeed;
        //LogInfo << "TrainAnaEngine checkPartion lstPartInfo[nPrePartionIndex].endframe" << lstPartInfo[nPrePartionIndex].endframe;
        //LogInfo << "TrainAnaEngine checkPartion bPartion == true lstPartInfo[nPrePartionIndex].fspeed:" << lstPartInfo[nPrePartionIndex].fspeed;
    }
    LogInfo << "TrainAnaEngine checkPartion end";
    return bPartion;
}

/**
* 计算车钩从中间到边缘的间隔帧
* inParam : 行车速度(单位:米/秒)
* inParam : 宽度
* inParam : 相机帧率(单位:帧/秒)
* outParam: N/A
* return  : 间隔帧
*/
int TrainAnaEngine::getOffsetFrame(float fspeed, int width, int nFrameRate)
{
    LogInfo << "TrainAnaEngine getOffsetFrame start";
    //LogInfo << "TrainAnaEngine getOffsetFrame fspeed:" << fspeed;
    //LogInfo << "TrainAnaEngine getOffsetFrame width:" << width;
    //LogInfo << "TrainAnaEngine getOffsetFrame nFrameRate:" << nFrameRate;
    //LogInfo << "TrainAnaEngine getOffsetFrame nLatestFrame:" << nLatestFrame;
    //偏移值 = (中间到边缘的宽度(米) / 速度(米/秒)->时间(秒))* 帧率(帧/秒)
    float ftmp = width * (float) nFrameRate;
    //LogInfo << "TrainAnaEngine getOffsetFrame start end:" << ftmp;
    ftmp = ftmp / fspeed;
    //LogInfo << "TrainAnaEngine getOffsetFrame start end:" << ftmp;
    int nRet = (int) ftmp;
    LogInfo << "TrainAnaEngine getOffsetFrame start end:" << nRet;
	return nRet;
}


/**
* 计算车钩移动的像素值
* inParam : 行车速度(单位:米/秒)
* inParam : 宽度
* inParam : 相机帧率(单位:帧/秒)
* outParam: N/A
* return  : 间隔帧
*/
int TrainAnaEngine::getCouplerOffsetPosition(float fspeed, int nframeindex)
{
    LogInfo << "TrainAnaEngine getCouplerOffsetPosition start";
    //LogInfo << "TrainAnaEngine getCouplerOffsetPosition fspeed:" << fspeed;
    //LogInfo << "TrainAnaEngine getCouplerOffsetPosition start:" << nframeindex;
    //单位换算
    // 米/秒 -> 米/帧
    // 米/帧 = 米/秒 * 秒/帧(即:1/帧率)
    float fmeter_frame = fspeed / nFrameRate;
    //LogInfo << "TrainAnaEngine getCouplerOffsetPosition fmeter_frame:" << fmeter_frame;
    // 米/帧 -> 像素/帧
    // 像素/帧 = 米/帧 * 像素/米
    float fpix_frame = fmeter_frame * (METHOD_BASE_WIDTH / TRAIN_IN_CAMERA_WIDTH);
    //LogInfo << "TrainAnaEngine getCouplerOffsetPosition fpix_frame:" << fpix_frame;

    int nretPixOffet = (int)fpix_frame;
    nretPixOffet = nretPixOffet * nframeindex;
    //LogInfo << "TrainAnaEngine getCouplerOffsetPosition nretPixOffet:" << nretPixOffet;
    LogInfo << "TrainAnaEngine getCouplerOffsetPosition end";
    return nretPixOffet;
}

int TrainAnaEngine::getTailPixOffset()
{
    LogInfo << "TrainAnaEngine getTailPixOffset start";
    // 单位计算
    // 帧宽像素/米
    float fframewidth_meter = ((METHOD_BASE_WIDTH * 1.0) / (TRAIN_IN_CAMERA_WIDTH));
    //LogInfo << "TrainAnaEngine getTailPixOffset fframewidth_meter:" << fframewidth_meter;
    // 车尾车钩像素位置
    float fOffsetPosistion = TRAIN_WIDTH * fframewidth_meter;
    //LogInfo << "TrainAnaEngine getTailPixOffset fOffsetPosistion:" << fOffsetPosistion;

    int nretOffset = (int)fOffsetPosistion;
    //LogInfo << "TrainAnaEngine getTailPixOffset nretOffset:" << nretOffset;
    //LogInfo << "TrainAnaEngine getTailPixOffset end";
    return nretOffset;
}


APP_ERROR TrainAnaEngine::Process()
{
    int iRet = APP_ERR_OK;
    //程序启动标志位
    bool bInited = false;
    while (!isStop_)
    {
        std::shared_ptr<void> pVoidData0 = nullptr;
        inputQueMap_[strPort0_]->pop(pVoidData0);
        if (nullptr == pVoidData0)
        {
            usleep(1000); //1ms
            continue;
        }

        LogInfo << "TrainAnaEngine Process start";
        std::shared_ptr<DecodedData> pDecodeData = std::static_pointer_cast<DecodedData>(pVoidData0);
        std::shared_ptr<void> pHostData = nullptr;

        //获取图像数据
        cv::Mat matYUV(pDecodeData->iHeight * 3 / 2, pDecodeData->iWidth, CV_8UC1);
        int iCopySize = pDecodeData->iWidth * pDecodeData->iHeight * 3 / 2;
        memcpy(matYUV.data, pHostData.get(), iCopySize);

        // 将YUV格式灰度化之后修改图像尺寸
	    cv::Mat cvtemp;
	    cv::cvtColor(matYUV, cvtemp, cv::COLOR_YUV2GRAY_420);
	    //cv::imwrite("/home/HwHiAiUser/1.jpg",cvtemp);
        matYUV = mtresizeImage(cvtemp, METHOD_BASE_WIDTH, METHOD_BASE_HEIGHT);
        //来车
        //cv::Mat baseimg = mtdecodeImageDatabyBin(lpimgdata, imglen);
        //cv::Mat baseimg = matYUV.data;
        //程序启动第一帧跳过 保存为基础图像
        if (bInited == false) {
            cvFirstImg = matYUV;
            bInited = true;
            nFrameRate = pDecodeData->iRate;
            continue;
        }
        LogInfo << "TrainAnaEngine Process pDecodeData.i64TimeStamp:" << pDecodeData->i64TimeStamp;
        ncurtime = pDecodeData->i64TimeStamp;
        //来车状态检测
        checkAction(matYUV, pDecodeData->i64TimeStamp);

        //判断是否有来车
        pDecodeData->iStatus = (nStatus == TRAIN_LEFT_RUN || nStatus == TRAIN_RIGHT_RUN || nStatus == TRAIN_UNKOWN_RUN || nStatus == TRAIN_RESTART) ? 1 : ( nStatus == TRAIN_PAUSE)? 2 : 0;
        //判断来车方向 0 方向未知/无车 1右侧来车(向左行驶:模型识别用) 2左侧来车(向右行驶:模型识别用)
        pDecodeData->iDirection = ((nStatus == TRAIN_LEFT_RUN) ? 1 : ((nStatus == TRAIN_RIGHT_RUN) ? 2 : 0));
        //列车是否通过
        pDecodeData->bIsEnd = (nStatus == TRAIN_PASSED || nStatus == TRAIN_RESTART ) ? true : false;
        LogInfo << "TrainAnaEngine Process pDecodeData->iStatus:" << pDecodeData->iStatus;
        //LogInfo << "TrainAnaEngine Process pDecodeData->iDirection:" << pDecodeData->iDirection;
        //新过车创建文件夹
        if ((nStatus != TRAIN_INIT_STATUS && nPreStatus == TRAIN_INIT_STATUS) ||
            ( nPreStatus == TRAIN_RESTART && nStatus == TRAIN_UNKOWN_RUN))
        {
            strTrainData_ = MyUtils::getins()->GetDate();
            strTrainName_ = MyUtils::getins()->GetTime();
            std::string strTrainPath = strResultPath_ + strTrainData_ + "/" + strTrainName_ + "/";
            MyUtils::getins()->CreateDirPath(strTrainPath);
        }
        //过车日期
        pDecodeData->strTrainDate = strTrainData_;
        //过车时间
        pDecodeData->strTrainName = strTrainName_;

       // iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pDecodeData));

        //当前帧是否有划分信息
        bool bPartionExist = false;
        
        //有车的状态进行车厢划分检测
        if (nStatus != TRAIN_INIT_STATUS){
            //LogInfo << "TrainAnaEngine Process nPartionPassFrameCount:" << nPartionPassFrameCount;
            //LogInfo << "TrainAnaEngine Process nSamePartionIgnoreCount:" << nSamePartionIgnoreCount;
            //检测到划分状态的情况下 进行跳帧处理
            //跳帧值为计算的车体通过三分之一之后的帧
            if (nPartionPassFrameCount < nSamePartionIgnoreCount) {
                nPartionPassFrameCount++;
            } else {
                //超过跳帧进行车厢划分检测
                bPartionExist = checkPartion(matYUV, pDecodeData->i64TimeStamp);
                //如果检测到车厢划分信息则检测跳帧的值
                if (bPartionExist == true) {
                    nPartionPassFrameCount = 0;
                }
            }
        }

        /// write json info to file

        //先读取文本内容，追加新的信息后再写入
        //划分信息 JSON格式
        Json::Value jvPartionInfo;
        //JSON保存路径
        std::string strFilePath;
        //当然车厢通过的数量
        int nPartionIndex = lstPartInfo.size() - 1;

        LogInfo << "TrainAnaEngine Process nPartionIndex:" << nPartionIndex;
        LogInfo << "TrainAnaEngine Process bPartionExist:" << bPartionExist;
        //检测到车厢划分信息
        if ((bPartionExist == true)) {
            strFilePath = strResultPath_ + pDecodeData->strTrainDate + "/" + pDecodeData->strTrainName + "/" 
                        + std::to_string(nPartionIndex + 1) + ".txt";
            // 记录过车日期
            jvPartionInfo["trainDate"] = pDecodeData->strTrainDate;
            // 记录过车时间
            jvPartionInfo["trainName"] = pDecodeData->strTrainName;
            // 记录车厢节数 (索引从0开始 所以这里+1)
            jvPartionInfo["trainNo"] = nPartionIndex + 1;
            // 记录行车开始帧
            jvPartionInfo["startFrameId"] = lstPartInfo[nPartionIndex].startframe;
            // 记录行车结束帧
            jvPartionInfo["endFrameId"] = lstPartInfo[nPartionIndex].endframe;
            // 记录车厢是否完全通过
            jvPartionInfo["isEnd"] = pDecodeData->bIsEnd;

            // 首部车钩的偏移位置 (单位帧)
            int headpos = 0;
            // 尾部车钩的偏移位置 (单位帧)
            int tailpos = (0 - nTailPixOffset);

            if (nPartionIndex == 0)
            {
                headpos = METHOD_BASE_WIDTH / 2;
                tailpos = tailpos + headpos;
            }
            // 是否位右侧来车
            bool brightcome = false;
            if (nStatus == TRAIN_RIGHT_RUN)
            {    
                brightcome = true;
                // 右侧来车 首部车钩从画面最右侧开始
                headpos = METHOD_BASE_WIDTH;
                // 右侧来车 尾部车钩从画面最右侧+车厢宽的像素值
                tailpos = METHOD_BASE_WIDTH + nTailPixOffset; 

                if (nPartionIndex == 0)
                {
                    headpos = METHOD_BASE_WIDTH / 2;
                    tailpos = tailpos - headpos;
                }
            }
            

            //LogInfo << "TrainAnaEngine Process lstPartInfo[nPartionIndex].startframe：" << lstPartInfo[nPartionIndex].startframe ;
            //LogInfo << "TrainAnaEngine Process lstPartInfo[nPartionIndex].endframe：" << lstPartInfo[nPartionIndex].endframe;
            //从当节车厢的开始帧到结束帧计算首部车钩和尾部车钩的偏移值
            for (int nplayframe = lstPartInfo[nPartionIndex].startframe; nplayframe <= lstPartInfo[nPartionIndex].endframe; nplayframe++)
            {
                Json::Value jvposInfo;
                // 当前车厢的第几几帧
                int noffsetindex = (nplayframe - lstPartInfo[nPartionIndex].startframe);
                // 根据车速计算车钩位置量(单位 像素)
                int noffsetpos = getCouplerOffsetPosition(lstPartInfo[nPartionIndex].fspeed, noffsetindex);
                // 初始化首部车钩偏移量(单位 像素)
                jvposInfo["headpos"] = -1;
                // 初始化尾部车钩偏移量(单位 像素)
                jvposInfo["tailpos"] = -1;
                
                if (brightcome == false) {
                    // 左侧来车 
                    // 首部车钩和尾部车钩 每帧加 车钩偏移值
                    jvposInfo["headpos"] = (headpos + noffsetpos);
                    jvposInfo["tailpos"] = (tailpos + noffsetpos);
                } else {
                    // 右侧来车 
                    // 首部车钩和尾部车钩 每帧减 车钩偏移值
                    jvposInfo["headpos"] = (headpos - noffsetpos);
                    jvposInfo["tailpos"] = (tailpos - noffsetpos);
                }
                //LogInfo << "TrainAnaEngine Process jvposInfo[headpos]" << jvposInfo["headpos"];
               // LogInfo << "TrainAnaEngine Process jvposInfo[tailpos]:" << jvposInfo["tailpos"];
                //LogInfo << "TrainAnaEngine Process jvPartionListInfo.append";
                jvPartionInfo[std::to_string(nplayframe)] = jvposInfo;
            }
            
            PartionInfo stTempInfo;
            // 开始记录新的一节车厢信息(从索引变成序号+1 ，新增一节车厢信息+1)
            stTempInfo.nindex = nPartionIndex + 2;
            // 上一节车厢的结束帧  - (偏移帧 = (镜头内的车体宽度/ (速度) -> 通过时间) * 帧/秒 ) 作为下一节车厢的开始帧
            int ntempOffsetFrame = lstPartInfo[nPartionIndex].endframe - (int)(((TRAIN_IN_CAMERA_WIDTH) / lstPartInfo[nPartionIndex].fspeed) * nFrameRate);
            //LogInfo << "TrainAnaEngine Process ntempOffsetFrame:" << ntempOffsetFrame;
            stTempInfo.startframe = (ntempOffsetFrame > nSamePartionIgnoreCount) ? ntempOffsetFrame : nSamePartionIgnoreCount;
            stTempInfo.i64StartTimeStamp = pDecodeData->i64TimeStamp;
            // 初始化下一节的结束帧
            //stTempInfo.endframe = 0;

            lstPartInfo.push_back(stTempInfo);
            // 记录当前车厢的信息到JSON文件
            //MyUtils::getins()->WriteJsonInfo(jvPartionInfo, strFilePath);
            std::shared_ptr<TrainRange> pTrainRange = std::make_shared<TrainRange>();
            pTrainRange->strTrainDate = jvPartionInfo["trainDate"].asString();
            pTrainRange->strTrainName = jvPartionInfo["trainName"].asString();
            pTrainRange->iTrainIndex = jvPartionInfo["trainNo"].asInt();
            pTrainRange->iStartFrameId = jvPartionInfo["startFrameId"].asInt();
            pTrainRange->iEndFrameId = jvPartionInfo["endFrameId"].asInt();
            pTrainRange->bIsEnd = jvPartionInfo["isEnd"].asBool();
            //iRet = outputQueMap_[strPort1_]->push(std::static_pointer_cast<void>(pTrainRange));    
        }

        // 有车的情况下。记录帧数：累加1
        if (nStatus != TRAIN_INIT_STATUS)
        {
            nLatestFrame = nLatestFrame + 1;
        }
        //停车或无车(初始化相关参数)


        /*
        if ((nStatus  == TRAIN_INIT_STATUS 
            && (nStatus != TRAIN_INIT_STATUS)) || pDecodeData->bIsEnd)
        {
            vResetPartion();
        }
        */
        // 车厢通过初始化状态
        if (pDecodeData->bIsEnd) {
            vResetPartion();
        }
        LogError<<"current img id   "<<num;
        num++;

        LogInfo << "TrainAnaEngine Process end";
    }
    return APP_ERR_OK;
}
