9-1-1車牌切割

9-1-1車牌切割

程式說明:

   此車牌辨識的專案是參考底下網址matering opencv第五章,並且沿用其中的一些資料庫,
   目前這邊是將其客製化成台灣的車牌,並且為了一一說明程式,將其拆成5個小專說明。
   參考網址: https://github.com/MasteringOpenCV


運行結果:

   這邊我們先對圖像做邊緣偵測,並且將其二值化,二值化完後用closing來連結圖塊,
   利用Contour找出物體輪廓,並判斷pixel數目不足於多少時或高於多少時剃除,保留下來的為mask,
   以此mask再將rgb的圖用洪水法將其圖塊塗滿,並裁剪下來,以便下專案可以做文字切割。

mask


範例程式:


#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <vector>
#include <algorithm>

bool verifyCarPlateSizes(cv::RotatedRect mAr)
{
    float error = 0.4; //車牌面積比例的誤差
    //一般台灣車牌的比例,初估為2.6
    float aspect = 2.6;
    //多大的面積pxiel忽略不偵測
    int min = 15 * aspect * 15;
    int max = 125 * aspect * 125;

    float rmin = aspect - aspect*error;
    float rmax = aspect + aspect*error;

    int area = mAr.size.height * mAr.size.width; //計算面積

    float r = (float)mAr.size.width / (float)mAr.size.height;
    if (r<1)
       r = (float)mAr.size.height / (float)mAr.size.width;

    if ((area < min || area > max) || (r < rmin || r > rmax))
    {
       return false;
    }
    else
    {
       return true;
    }

}

cv::Mat histeq(cv::Mat in)
{
    cv::Mat out(in.size(), in.type());
    if (in.channels() == 3){
       cv::Mat hsv;
       std::vector hsvSplit;
       cv::cvtColor(in, hsv, CV_BGR2HSV);
       cv::split(hsv, hsvSplit);
       cv::equalizeHist(hsvSplit[2], hsvSplit[2]);
       cv::merge(hsvSplit, hsv);
       cv::cvtColor(hsv, out, CV_HSV2BGR);
    }
    else if (in.channels() == 1)
    {
       cv::equalizeHist(in, out);
    }

    return out;
}

void carPlateSegment(cv::Mat& carImg, cv::Mat& carImgColor, int& num)
{

    //先除雜訊
    cv::blur(carImg, carImg, cv::Size(5, 5));

    //先做sobel找出邊緣
    cv::Mat sobleImg;

    cv::Sobel(carImg, sobleImg, CV_8U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);

    //將偵測出邊緣的圖片二值化
    cv::Mat binartEdgeImg;
    cv::threshold(sobleImg, binartEdgeImg, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);

    //利用closing結合邊緣試著將多邊緣區域結合成一圖塊(blob)     cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(17, 3));     cv::morphologyEx(binartEdgeImg, binartEdgeImg, CV_MOP_CLOSE, element);
    //找連結的圖塊的輪廓
    std::vector< std::vector< cv::Point> > contours;
    findContours(binartEdgeImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    //開始處理偵測到的輪廓
    std::vector >::iterator itc = contours.begin();
    std::vector rects;

    //Remove patch that are no inside limits of aspect ratio and area.
    while (itc != contours.end())
    {
       //Create bounding rect of object
       cv::RotatedRect mAr = cv::minAreaRect(cv::Mat(*itc));
       if (!verifyCarPlateSizes(mAr))
       {
          itc = contours.erase(itc);
       }
       else
       {
          ++itc;
          rects.push_back(mAr);
       }
    }

    cv::Mat result;
    carImgColor.copyTo(result);
    cv::drawContours(result, contours,
    -1, // draw all contours
    cv::Scalar(255, 0, 0), // in blue
    1); // with a thickness of 1

    for (int i = 0; i< rects.size(); i++)
    {

       //為了找到更適合的車牌區間,利用洪水法的方法來填充相似的色塊,以及配合contour找出的圖塊邊緣來判斷
       cv::circle(result, rects[i].center, 3, cv::Scalar(0, 255, 0), -1);
       //get the min size between width and height
       float minSize = (rects[i].size.width < rects[i].size.height) ? rects[i].size.width : rects[i].size.height;
       minSize = minSize - minSize*0.5;        cv::Mat mask;
       mask.create(carImgColor.rows + 2, carImgColor.cols + 2, CV_8UC1);
       mask = cv::Scalar::all(0);
       int loDiff = 30;
       int upDiff = 30;
       int connectivity = 4;
       int newMaskVal = 255;
       int NumSeeds = 10;
       cv::Rect ccomp;
       int flags = connectivity + (newMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
       for (int j = 0; j        {
          cv::Point seed;
          seed.x = rects[i].center.x + rand() % (int)minSize - (minSize / 2);
          seed.y = rects[i].center.y + rand() % (int)minSize - (minSize / 2);
          cv::circle(result, seed, 1, cv::Scalar(0, 255, 255), -1);
          int area = floodFill(carImgColor, mask, seed, cv::Scalar(255, 0, 0), &ccomp, cv::Scalar(loDiff, loDiff, loDiff), cv::Scalar(upDiff, upDiff, upDiff), connectivity | (newMaskVal << 8) | CV_FLOODFILL_FIXED_RANGE | CV_FLOODFILL_MASK_ONLY);
       }

       cv::imshow("MASK", mask);
       cv::waitKey(0);


       //Check new floodfill mask match for a correct patch.
       //Get all points detected for get Minimal rotated Rect
       std::vector pointsInterest;
       cv::Mat_::iterator itMask = mask.begin();
       cv::Mat_::iterator end = mask.end();
       for (; itMask != end; ++itMask)
          if (*itMask == 255)
             pointsInterest.push_back(itMask.pos());

       cv::RotatedRect minRect = minAreaRect(pointsInterest);

       if (verifyCarPlateSizes(minRect))
       {
          // rotated rectangle drawing
          cv::Point2f rect_points[4]; minRect.points(rect_points);
          for (int j = 0; j < 4; j++)
             line(result, rect_points[j], rect_points[(j + 1) % 4], cv::Scalar(0, 0, 255), 1, 8);

          //Get rotation matrix
          float r = (float)minRect.size.width / (float)minRect.size.height;
          float angle = minRect.angle;
          if (r<1)
             angle = 90 + angle;
          cv::Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1);

          //Create and rotate image
          cv::Mat img_rotated;
          cv::warpAffine(carImgColor, img_rotated, rotmat, carImgColor.size(), CV_INTER_CUBIC);

          //Crop image
          cv::Size rect_size = minRect.size;
          if (r < 1)
             std::swap(rect_size.width, rect_size.height);
          cv::Mat img_crop;
          getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);

          cv::Mat resultResized;
          resultResized.create(33, 110, CV_8UC3);
          resize(img_crop, resultResized, resultResized.size(), 0, 0, cv::INTER_CUBIC);
          //Equalize croped image
          cv::Mat grayResult;
          cv::cvtColor(resultResized, grayResult, CV_BGR2GRAY);
          cv::blur(grayResult, grayResult, cv::Size(3, 3));
          grayResult = histeq(grayResult);

          std::string filename = "file";
          std::stringstream numS;
          numS << i;
          filename = filename + numS.str() + ".bmp";
          cv::imshow("carPlate", grayResult);
          cv::waitKey(0);
          cv::imwrite(filename, grayResult);
          num++;
       }
    }

}


int main(int argc, char** argv)
{
    cv::Mat carImgInColor = cv::imread("car01.jpg", 1);

    cv::Mat carImgIn = cv::imread("car01.jpg", 0);

    cv::Mat carImg, carImgColor;

    cv::resize(carImgIn, carImg, cv::Size(800,600));
    cv::resize(carImgInColor, carImgColor, cv::Size(800, 600));

    int num = 0;

    carPlateSegment(carImg, carImgColor, num);
    return 0;
}