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进行确定哪一张是车牌区域。