#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace cv;
using namespace std;
Mat getHSVMask(Mat& srcImage)
{
int height = srcImage.rows; //原图的高度
int width = srcImage.cols; //原图的宽度
double h, s, v;
uchar r, g, b;
uchar max, min, delta;
Mat hsvMask = Mat::zeros(srcImage.size(), CV_8UC1);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
b = srcImage.at<Vec3b>(i, j)[0];
g = srcImage.at<Vec3b>(i, j)[1];
r = srcImage.at<Vec3b>(i, j)[2];
max = (r>g) ? r : g;
max = (max>b) ? max : b;
min = (r<g) ? r : g;
min = (min<b) ? min : b;
delta = max - min;
if (delta == 0)
h = 0;
else
{
if (max == r && g >= b)
h = double(60 * (g - b)) / delta;
if (max == r && g<b)
h = double(60 * (g - b)) / delta + 360;
if (max == g)
h = double(60 * (b - r)) / delta + 120;
if (max == b)
h = double(60 * (r - g)) / delta + 240;
}
v = max;
if (v == 0)
s = 0;
else
s = double(delta) / max;
if (h<0)
h += 360;
if (h <= 270 && h >= 180 && s>0.30)
{
hsvMask.at<uchar>(i, j) = 255;
}
if (h <= 270 && h >= 205 && s>0.48)
{
hsvMask.at<uchar>(i, j) = 255;
}
}
}
//imshow("hsvMask", hsvMask); waitKey(100);
return hsvMask;
}
Mat getSuspendEdge(Mat& hsvMask, Mat& cannyImage)
{
int height = hsvMask.rows;
int width = hsvMask.cols;
Mat suspendEdge = Mat::zeros(hsvMask.size(), hsvMask.type());
Mat element_erode = getStructuringElement(cv::MORPH_ELLIPSE, Size(5, 5)); //腐蚀的参数
Mat element_dilate = getStructuringElement(cv::MORPH_ELLIPSE, Size(5, 5)); //膨胀的参数
//进行一次开操作消除杂碎点 开运算的作用是为了去掉细节
erode(hsvMask, hsvMask, element_erode); //腐蚀
dilate(hsvMask, hsvMask, element_dilate); //膨胀
//车牌疑似区域提取
for (int i = 1; i != height - 2; i++)
{
for (int j = 1; j != width - 2; j++)
{
Rect rct;
rct.x = j - 1;
rct.y = i - 1;
rct.height = 3;
rct.width = 3;
if ((cannyImage.at<uchar>(i, j) == 255) && (cv::countNonZero(hsvMask(rct)) >= 3))
suspendEdge.at<uchar>(i, j) = 255;
}
}
//imshow("suspendEdge", suspendEdge); waitKey(100);
return suspendEdge;
}
Mat getFinalMask(Mat& suspendEdge)
{
Mat finalMask = suspendEdge.clone();
int height = finalMask.rows;
int width = finalMask.cols;
//再对得到的边缘进行一次闭运算,得到车牌的掩模
Mat element_erode = getStructuringElement(cv::MORPH_ELLIPSE, Size(0.025*width, 0.025*height));
Mat element_dilate = getStructuringElement(cv::MORPH_ELLIPSE, Size(0.025*width, 0.025*height));
dilate(finalMask, finalMask, element_dilate); //膨胀
erode(finalMask, finalMask, element_erode); //腐蚀
//imshow("finalMask", finalMask); waitKey(100);
return finalMask;
}
Mat getArea(Mat& srcImage, Mat& finalMask)
{
int height = finalMask.rows;
int width = finalMask.cols;
vector<vector<Point> > contours;
findContours(finalMask.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<Rect> candidates;//候选
int best_i = -1;
int best_area = 0;
double ratio_high = 0.3;//比例
double ratio_low = 0.1;
for (int i = 0; i != contours.size(); i++)//轮廓上点的个数
{
Rect rect = boundingRect(contours[i]);//计算轮廓的最小外接正矩形
int area = countNonZero(finalMask(rect));//对二值化图像执行countNonZero,可得到非零像素点数
double ratio = double(area) / rect.area();//非零像素点数占最小外接正矩形的比例
double wh_ratio = double(rect.width) / rect.height;//最小外接正矩形的长(width)宽(height)比
if (ratio > ratio_high && wh_ratio > 2 && wh_ratio < 5 && rect.height > 25 && rect.width > 75)
{
if (area > best_area)
{
best_area = area;
best_i = i;
}
}
else if (ratio > ratio_low && wh_ratio > 2 && wh_ratio < 5 && rect.height > 25 && rect.width > 75)
{
Mat element_dilate = getStructuringElement(cv::MORPH_ELLIPSE, Size(0.02*width, 0.02*height));
Mat tmp = finalMask.clone();
dilate(tmp, tmp, element_dilate); //膨胀
area = countNonZero(tmp(rect));
ratio = double(area) / rect.area();
if (ratio > ratio_high && wh_ratio > 2 && wh_ratio < 5 && rect.height > 25 && rect.width > 75)
{
if (area > best_area)
{
best_area = area;
best_i = i;
}
}
}
}
if (best_i < 0)
cout << "no area found!" << endl;
Rect rect = boundingRect(contours[best_i]);
cout<<"left-top coordinates:("<<rect.x<<","<<rect.y<<") " <<"rect.width:"<< rect.width << " " <<"rect.height:"<< rect.height << endl;
rect.x -= rect.width * 0.05;
rect.y -= rect.height * 0.05;
rect.width += rect.width * 0.1;
rect.height += rect.height * 0.1;
Mat Image = srcImage.clone();
cv::rectangle(Image, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 0, 255), 1, 1, 0); //画出车牌的位置
imshow("Image", Image); waitKey(5);
Mat resultArea = srcImage(rect);
resize(resultArea, resultArea, Size(200,70), 0, 0, CV_INTER_LINEAR);
//imshow("resultArea", resultArea); waitKey(100);
return resultArea;
}
int main(void)
{
char name[10];
for (int i = 1; i <= 10; i++)
{
sprintf(name, "%d.jpg", i);
Mat srcImage = imread(name); //读取图片
resize(srcImage, srcImage, Size(800, 600), 0, 0, CV_INTER_LINEAR); //对图片的大小进行统一
//imshow("srcImage", srcImage); //显示读取的图片
Mat grayImage; //获取灰度图像
cvtColor(srcImage, grayImage, CV_RGB2GRAY); //通过cvtColor函数将RGB空间转换成灰度图
GaussianBlur(grayImage, grayImage, Size(3, 3), 0, 0, cv::BORDER_DEFAULT); //高斯滤波
Mat cannyImage; //通过Canny算子检测边缘
int edgeThresh = 50;
Canny(grayImage, cannyImage, edgeThresh, edgeThresh * 3, 3);
//imshow("cannyImage", cannyImage); waitKey(5);
Mat hsvMask = getHSVMask(srcImage); //筛选符合HSV条件的区域,并进行一次开运算消除杂碎点
Mat suspendEdge = getSuspendEdge(hsvMask, cannyImage);
Mat finalMask = getFinalMask(suspendEdge);
Mat resultArea = getArea(srcImage, finalMask);
}
waitKey();
}