分割车牌

cv::Mat src = cv::imread("2.jpg" , 1);

    cv::Mat img_gray;
    cv::cvtColor(src, img_gray , CV_BGR2GRAY);
    // 图像滤波处理
    cv::blur(img_gray, img_gray, cv::Size(5, 5));

    cv::Mat src_filter;
    cv::Sobel(img_gray, src_filter, CV_8U, 0, 1, 3, 1, 0);

    // 阈值分割二值化
    cv::Mat img_threshold;
    cv::threshold(src_filter, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);

    //获取指定形状和尺寸的结构元素,因为车牌长宽比大约是5:1所以这里设置膨胀的内核尺寸为(15,3)
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 3));
    // 闭运算
    morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);

    // 查找轮廓
    std::vector < std::vector<cv::Point> > contours;
    cv::findContours(img_threshold, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    // 保存符合要求的旋转矩形
    std::vector<cv::RotatedRect> rects;

    std::vector<std::vector<cv::Point> >::iterator iter = contours.begin();
    while (iter != contours.end())
    {
        // 创建一个旋转举行
        cv::RotatedRect rrt = cv::minAreaRect(cv::Mat(*iter));
        if (verifySize(rrt))
        {
            ++iter;
            rects.push_back(rrt);
        }
        else
        {
            iter = contours.erase(iter);
        }
    }

    // 在原始图像上画轮廓
    cv::Mat result;
    src.copyTo(result);
    cv::drawContours(result, contours, -1, cv::Scalar(255,0,0), 1);

    std::vector<cv::Mat> segment_mat;
    for (int i = 0 ; i < rects.size() ; ++i)
    {
        cv::circle(result, rects[i].center, 3, cv::Scalar(0 , 255 , 0));
        // 获得矩形宽度和高度中的最小值
        float minSize = (rects[i].size.width < rects[i].size.height ? rects[i].size.width : rects[i].size.height);
        minSize = minSize - 0.5 * minSize;
        srand(time(0));

        // 漫水填充
        cv::Mat mask;
        mask.create(src.rows + 2, src.cols + 2, CV_8UC1);
        mask = cv::Scalar::all(0);

        // 漫水填充亮度之间的差异
        int lowDiff = 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 < NumSeeds ; ++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);
            circle(result, seed, 1, cv::Scalar(0 , 255 , 255), -1);

            // 指定颜色填充一个连接域
            int area = floodFill(src, mask, seed, cv::Scalar(255 , 0 , 0), &ccomp, cv::Scalar(lowDiff , lowDiff , lowDiff), cv::Scalar(upDiff , upDiff , upDiff), flags);
        }

        // 得到旋转矩形
        std::vector < cv::Point> pointInterest;
        cv::Mat_<uchar>::iterator itMask = mask.begin<uchar>();
        cv::Mat_<uchar>::iterator end = mask.end<uchar>();
        for (; itMask != end ; ++itMask)
        {
            if (*itMask == 255)
            {
                pointInterest.push_back(itMask.pos());
            }
        }

        // 计算旋转最小面积的包围矩形
        cv::RotatedRect minRect = cv::minAreaRect(pointInterest);
        if (verifySize(minRect))
        {
            // 旋转矩形的四个顶点
            cv::Point2f rect_points[4];
            minRect.points(rect_points);
            for (int j = 0; j < 4; ++j)
            {
                cv::line(result, rect_points[j], rect_points[(j + 1) % 4], cv::Scalar(0 , 0 ,255), 1, 8);
            }
            // 获取旋转矩形的旋转角度
            float raspect = (float)rects[i].size.width / (float)rects[i].size.height;
            float angle = minRect.angle;
            std::cout << "旋转角度:" << angle << std::endl;

            if (raspect < 1)
            {
                angle = 90 + angle;
            }
            // 获取旋转矩阵
            cv::Mat rotmat = cv::getRotationMatrix2D(minRect.center, angle, 1.0);

            // 对图像做仿射变换
            cv::Mat img_rotated;
            cv::warpAffine(src, img_rotated, rotmat, src.size(), CV_INTER_CUBIC);

            // 裁剪感兴趣区域
            cv::Size rect_size = minRect.size;
            if (raspect < 1)
            {
                std::swap(rect_size.width, rect_size.height);
            }

            cv::Mat img_crop;
            cv::getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);

            cv::Mat resultResized;
            resultResized.create(33, 144, CV_8U);
            cv::resize(img_crop, resultResized, resultResized.size(), 0, 0, 1);

            cv::Mat grayResult;
            cv::cvtColor(resultResized, grayResult, CV_BGR2GRAY);
            cv::blur(grayResult, grayResult, cv::Size(3, 3));
            cv::equalizeHist(grayResult, grayResult);

            segment_mat.push_back(grayResult);
        }   
    }

    for (int i = 0 ; i < segment_mat.size() ; ++i)
    {
        char name[40] = { 0 };
        sprintf(name, "%d", i);
        cv::imshow(name, segment_mat[i]);
    }
    cv::waitKey(0);

因为车牌的长宽比符合一定比例,我们以此为条件检查所有的轮廓

bool verifySize(cv::RotatedRect rrt)
{
    // 定义车牌大致的长宽比,已知车牌大致比例为52 * 11
    float aspect = 4.7272;

    int minAear = 15 * aspect * 15;
    int maxAear = 125 * aspect * 125;

    int area = rrt.size.width * rrt.size.height;
    // 宽高比
    float r = (float)rrt.size.width / (float)rrt.size.height;
    if (r < 1)
    {
        r = (float)rrt.size.height / (float)rrt.size.width;
    }

    float aspect_min = aspect - 0.4 * aspect;
    float aspect_max = aspect + 0.4 * aspect;

    if (area < minAear || area > maxAear || r > aspect_max || r < aspect_min)
    {
        return false;
    }
    else
    {
        return true;
    }
}

运行得结果为:
这里写图片描述
经过条件筛选分割出两个兴趣区,下一步就是就是识别这两个区域,可以用SVM进行确定哪一张是车牌区域。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值