EasyPR如何添加绿牌C++版

        终于答辩结束了,这个项目忘的也差不多了,今天正巧没事准备整理一下,本人菜鸡一枚,本科毕业设计题目是《基于OpenCV的车牌识别系统》,这个项目差不多研究了半个月才看懂,当时我给导师看这个项目的时候,导师直接说这些东西都是别人的,你什么都不做,这工作量有点少,怕我答辩时候过不了,之后就是让我加功能,(你这个可以检测绿牌吗?还有你这个识别算法,用的是什么,能不能用其他的,建议加个绿牌,并且做个其他识别算法和你这个对比一下,工作量看起来多一些),还是太懒了我只加了绿牌,之后又被导师数落了一顿。
        这个项目写的听清楚的,还有开发者文档参考,作者也把各个版本的配置方式贴出来了。我自己只是用MFC画了一个界面,里面就是调作者写好的函数,我自己加了绿牌,修改了部分代码。

EasyPR如何添加绿牌C++版_第1张图片

1.添加绿色

        在枚举颜色里面添加GREEN。在config.h文件中:

enum Color { BLUE, YELLOW, WHITE, GREEN,UNKNOWN };

EasyPR如何添加绿牌C++版_第2张图片

2.添加绿色HSV

       这里我设置的是35-80,在core_func.cpp文件中colorMatch函数中添加。修改后的代码如下:

Mat colorMatch(const Mat &src, Mat &match, const Color r,
    const bool adaptive_minsv) {

    // if use adaptive_minsv
    // min value of s and v is adaptive to h
    const float max_sv = 255;
    const float minref_sv = 64;

    const float minabs_sv = 95; //95;

    // H range of blue 

    const int min_blue = 100;  // 100
    const int max_blue = 140;  // 140

    // H range of yellow

    const int min_yellow = 15;  // 15
    const int max_yellow = 40;  // 40

    // H range of white

    const int min_white = 0;   // 15
    const int max_white = 30;  // 40

     //Louis add,green  
     // H range of green

    const int min_green = 35;   // 35
    const int max_green = 80;   // 80

    Mat src_hsv;

    // convert to HSV space
    cvtColor(src, src_hsv, CV_BGR2HSV);

    std::vector<cv::Mat> hsvSplit;
    split(src_hsv, hsvSplit);
    equalizeHist(hsvSplit[2], hsvSplit[2]);
    merge(hsvSplit, src_hsv);

    // match to find the color

    int min_h = 0;
    int max_h = 0;
    switch (r) {
    case BLUE:
      min_h = min_blue;
      max_h = max_blue;
      break;
    case YELLOW:
      min_h = min_yellow;
      max_h = max_yellow;
      break;
    case WHITE:
      min_h = min_white;
      max_h = max_white;
      break;
      //Louis add,green  
    case GREEN:
        min_h = min_green;
        max_h = max_green;
        break;
    default:
      // Color::UNKNOWN
      break;
    }

    float diff_h = float((max_h - min_h) / 2);
    float avg_h = min_h + diff_h;

    int channels = src_hsv.channels();
    int nRows = src_hsv.rows;

    // consider multi channel image
    int nCols = src_hsv.cols * channels;
    if (src_hsv.isContinuous()) {
      nCols *= nRows;
      nRows = 1;
    }

    int i, j;
    uchar* p;
    float s_all = 0;
    float v_all = 0;
    float count = 0;
    for (i = 0; i < nRows; ++i) {
      p = src_hsv.ptr<uchar>(i);
      for (j = 0; j < nCols; j += 3) {
        int H = int(p[j]);      // 0-180
        int S = int(p[j + 1]);  // 0-255
        int V = int(p[j + 2]);  // 0-255

        s_all += S;
        v_all += V;
        count++;

        bool colorMatched = false;

        if (H > min_h && H < max_h) {
          float Hdiff = 0;
          if (H > avg_h)
            Hdiff = H - avg_h;
          else
            Hdiff = avg_h - H;

          float Hdiff_p = float(Hdiff) / diff_h;

          float min_sv = 0;
          if (true == adaptive_minsv)
            min_sv =
            minref_sv -
            minref_sv / 2 *
            (1
            - Hdiff_p);  // inref_sv - minref_sv / 2 * (1 - Hdiff_p)
          else
            min_sv = minabs_sv;  // add

          if ((S > min_sv && S < max_sv) && (V > min_sv && V < max_sv))
            colorMatched = true;
        }

        if (colorMatched == true) {
          p[j] = 0;
          p[j + 1] = 0;
          p[j + 2] = 255;
        }
        else {
          p[j] = 0;
          p[j + 1] = 0;
          p[j + 2] = 0;
        }
      }
    }

    // cout << "avg_s:" << s_all / count << endl;
    // cout << "avg_v:" << v_all / count << endl;

    // get the final binary

    Mat src_grey;
    std::vector<cv::Mat> hsvSplit_done;
    split(src_hsv, hsvSplit_done);
    src_grey = hsvSplit_done[2];

    match = src_grey;

    return src_grey;
  }

3.getPlateType中添加绿色

这部分也在core_func.cpp文件中。

Color getPlateType(const Mat &src, const bool adaptive_minsv) {
    float max_percent = 0;
    Color max_color = UNKNOWN;

    float blue_percent = 0;
    float yellow_percent = 0;
    float white_percent = 0;
    float green_percent = 0;


    if (plateColorJudge(src, BLUE, adaptive_minsv, blue_percent) == true) {
      // cout << "BLUE" << endl;
      return BLUE;
    } else if (plateColorJudge(src, YELLOW, adaptive_minsv, yellow_percent) ==
               true) {
      // cout << "YELLOW" << endl;
      return YELLOW;
    } else if (plateColorJudge(src, WHITE, adaptive_minsv, white_percent) ==
               true) {
      // cout << "WHITE" << endl;
      return WHITE;
    }
    else if (plateColorJudge(src, GREEN, adaptive_minsv, green_percent) ==
        true) {
        // cout << "GREEN" << endl;
        return GREEN;
    }
    else {
      //std::cout << "OTHER" << std::endl;

      /*max_percent = blue_percent > yellow_percent ? blue_percent : yellow_percent;
      max_color = blue_percent > yellow_percent ? BLUE : YELLOW;
      max_color = max_percent > white_percent ? max_color : WHITE;*/

      // always return green
      return GREEN;
    }
  }

4.颜色定位中添加绿色

plate_locate.cpp中plateColorLocate函数,修改后如下:

int CPlateLocate::plateColorLocate(Mat src, vector<CPlate> &candPlates,
                                   int index) {
  vector<RotatedRect> rects_color_blue;
  rects_color_blue.reserve(64);
  vector<RotatedRect> rects_color_yellow;
  rects_color_yellow.reserve(64);
  vector<RotatedRect> rects_color_green;
  rects_color_green.reserve(64);

  vector<CPlate> plates_blue;
  plates_blue.reserve(64);
  vector<CPlate> plates_yellow;
  plates_yellow.reserve(64);
  vector<CPlate> plates_green;
  plates_green.reserve(64);

  Mat src_clone = src.clone();

  Mat src_b_blue;
  Mat src_b_yellow;
  Mat src_b_green;
#pragma omp parallel sections
  {
#pragma omp section
    {
      colorSearch(src, BLUE, src_b_blue, rects_color_blue);
      deskew(src, src_b_blue, rects_color_blue, plates_blue, true, BLUE);
      //imshow("blue", src_b_blue);
    }
#pragma omp section
    {
      colorSearch(src_clone, YELLOW, src_b_yellow, rects_color_yellow);
      deskew(src_clone, src_b_yellow, rects_color_yellow, plates_yellow, true, YELLOW);
    }
#pragma omp section
    {
        colorSearch(src_clone, GREEN, src_b_green, rects_color_green);
        deskew(src_clone, src_b_green, rects_color_green, plates_green, true, GREEN);
        //imshow("green", src_b_green);
    }
  }

  candPlates.insert(candPlates.end(), plates_blue.begin(), plates_blue.end());
  candPlates.insert(candPlates.end(), plates_yellow.begin(), plates_yellow.end());
  candPlates.insert(candPlates.end(), plates_green.begin(), plates_green.end());

  return 0;
}

以上处理完基本可以实现对绿牌的定位,如果需要更高的准确率,可以自己重新训练一下,加上绿牌,因为绿牌的大小,颜色特征等都和蓝牌差距太大。

5.字符分割部分

这部分主要更改个分割字符数量和二值化参数,因为绿牌是8个字符,蓝牌7个字符,并且绿牌是背景颜色浅,车牌号颜色深,蓝牌是背景颜色深,车牌号颜色浅,所以蓝牌进行反二值化,绿牌则是正二值化。如下是core.func.cpp文件中。

void spatial_ostu(InputArray _src, int grid_x, int grid_y, Color type) {
    Mat src = _src.getMat();

    int width = src.cols / grid_x;
    int height = src.rows / grid_y;

    // iterate through grid
    for (int i = 0; i < grid_y; i++) {
      for (int j = 0; j < grid_x; j++) {
        Mat src_cell = Mat(src, Range(i * height, (i + 1) * height), Range(j * width, (j + 1) * width));
        if (type == BLUE) {
          cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
        } else if (type == YELLOW) {
          cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
        } else if (type == WHITE) {
          cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
        }
        else if (type == GREEN) {
            cv::threshold(src_cell, src_cell, 0, 255, THRESH_OTSU + CV_THRESH_BINARY_INV);
        }
        else {
          cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
        }
      }
    }
  }

如下是字符分割函数修改后,在chars_segment.cpp中:

int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color) {
  if (!input.data) return 0x01;

  Color plateType = color;

  Mat input_grey;
  cvtColor(input, input_grey, CV_BGR2GRAY);

 //imshow("cvt", input_grey);


  if (0) {
    imshow("plate", input_grey);
    waitKey(0);
    destroyWindow("plate");
  }

  Mat img_threshold;

  img_threshold = input_grey.clone();
  spatial_ostu(img_threshold, 8, 2, plateType);
  //imshow("ostu", img_threshold);

  if (0) {
    imshow("plate", img_threshold);
    waitKey(0);
    destroyWindow("plate");
  }

  // remove liuding and hor lines
// also judge weather is plate use jump count
  if (!clearLiuDing(img_threshold)) return 0x02;

  Mat img_contours;
  img_threshold.copyTo(img_contours);

  vector<vector<Point> > contours;
  findContours(img_contours,
               contours,               // a vector of contours
               CV_RETR_EXTERNAL,       // retrieve the external contours
               CV_CHAIN_APPROX_NONE);  // all pixels of each contours
  //  Mat dst = input;
  //for (vector point : contours) {
  //    RotatedRect rotatedRect = minAreaRect(point);
  //    rectangle(dst, rotatedRect.boundingRect(), Scalar(255,0,255));
  //}
  //imshow("dst", dst);

  vector<vector<Point> >::iterator itc = contours.begin();
  vector<Rect> vecRect;

  while (itc != contours.end()) {
    Rect mr = boundingRect(Mat(*itc));
    Mat auxRoi(img_threshold, mr);

    if (verifyCharSizes(auxRoi)) vecRect.push_back(mr);
    ++itc;
  }


  if (vecRect.size() == 0) return 0x03;

  vector<Rect> sortedRect(vecRect);
  std::sort(sortedRect.begin(), sortedRect.end(),
            [](const Rect& r1, const Rect& r2) { return r1.x < r2.x; });

  size_t specIndex = 0;
  if(color == GREEN)
        specIndex = GetSpecificRectNew(sortedRect);//new car
  else
        specIndex = GetSpecificRect(sortedRect);//old car

  Rect chineseRect;
  if (specIndex < sortedRect.size())
    chineseRect = GetChineseRect(sortedRect[specIndex]);
  else
    return 0x04;

  if (0) {
    rectangle(img_threshold, chineseRect, Scalar(255));
    imshow("plate", img_threshold);
    waitKey(0);
    destroyWindow("plate");
  }

  vector<Rect> newSortedRect;
  newSortedRect.push_back(chineseRect);

  if (color == GREEN)
        RebuildRectNew(sortedRect, newSortedRect, specIndex);
  else
        RebuildRect(sortedRect, newSortedRect, specIndex);

  if (newSortedRect.size() == 0) return 0x05;

  Mat dst = input;

  bool useSlideWindow = true;
  bool useAdapThreshold = true;
  //bool useAdapThreshold = CParams::instance()->getParam1b();

  for (size_t i = 0; i < newSortedRect.size(); i++) {
    Rect mr = newSortedRect[i];
    //rectangle(dst, mr, Scalar(255, 0, 255));
    // Mat auxRoi(img_threshold, mr);
    Mat auxRoi(input_grey, mr);
    Mat newRoi;

    if (i == 0) {
      if (useSlideWindow) {
        float slideLengthRatio = 0.1f;
        //float slideLengthRatio = CParams::instance()->getParam1f();
        if (!slideChineseWindow(input_grey, mr, newRoi, plateType, slideLengthRatio, useAdapThreshold))
          judgeChinese(auxRoi, newRoi, plateType);
      }
      else
        judgeChinese(auxRoi, newRoi, plateType);
    }
    else {
      if (BLUE == plateType) {
        threshold(auxRoi, newRoi, 0, 255, CV_THRESH_BINARY + CV_THRESH_OTSU);
      }
      else if (YELLOW == plateType) {
        threshold(auxRoi, newRoi, 0, 255, CV_THRESH_BINARY_INV + CV_THRESH_OTSU);
      }
      else if (WHITE == plateType) {
        threshold(auxRoi, newRoi, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
      }
      else if (GREEN == plateType) {
          threshold(auxRoi, newRoi, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
      }
      else {
        threshold(auxRoi, newRoi, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
      }

      newRoi = preprocessChar(newRoi);
    }

    if (0) {
      if (i == 0) {
        imshow("input_grey", input_grey);
        waitKey(0);
        destroyWindow("input_grey");
      }
      if (i == 0) {
        imshow("newRoi", newRoi);
        waitKey(0);
        destroyWindow("newRoi");
      }
    }

    resultVec.push_back(newRoi);
  }
  //imshow("dst", dst);
  return 0;
}

添加如下两个函数,因为字符数不同,切割函数也不相同。

int CCharsSegment::GetSpecificRectNew(const vector<Rect>& vecRect) {
    vector<int> xpositions;
    int maxHeight = 0;
    int maxWidth = 0;

    for (size_t i = 0; i < vecRect.size(); i++) {
        xpositions.push_back(vecRect[i].x);

        if (vecRect[i].height > maxHeight) {
            maxHeight = vecRect[i].height;
        }
        if (vecRect[i].width > maxWidth) {
            maxWidth = vecRect[i].width;
        }
    }

    int specIndex = 0;
    for (size_t i = 0; i < vecRect.size(); i++) {
        Rect mr = vecRect[i];
        int midx = mr.x + mr.width / 2;

        // use prior knowledage to find the specific character
        // position in 1/8 and 2/8
        if ((mr.width > maxWidth * 0.6 || mr.height > maxHeight * 0.6) &&
            (midx < int(m_theMatWidth / 8.15f) * kSymbolIndex &&
                midx > int(m_theMatWidth / 8.15f) * (kSymbolIndex - 1))) {
            specIndex = i;
        }
    }

    return specIndex;
}


int CCharsSegment::RebuildRectNew(const vector<Rect>& vecRect,
    vector<Rect>& outRect, int specIndex) {
    int count = 7;//Louis changed 6->7, for green plate car
    for (size_t i = specIndex; i < vecRect.size() && count; ++i, --count) {
        outRect.push_back(vecRect[i]);
    }

    return 0;
}
}

以上改完基本可以实现对绿牌的识别,我只改了我用到的部分函数,其他未修改的同理。好久没看这个项目了,应该改的就这么多了,还有就是我训练的时候只用了10几张绿牌,关键是没数据集。。。

你可能感兴趣的:(Opencv)