编程语言
首页 > 编程语言> > [opencv完整项目详解] 传统图像算法解决路标的检测和识别

[opencv完整项目详解] 传统图像算法解决路标的检测和识别

作者:互联网

前言:
这是数字图像课程的大作业,老师要求不可以采用深度学习的方法检测和识别特定的路标,只能采用传统的图像算法提取特征从而检测出特定的车牌.

完整代码:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>

using namespace std;
using namespace cv;

#define PI 3.1415926  

struct BGR	// 定义BGR结构体
{
	uchar b;
	uchar g;
	uchar r;
};

struct HSV // 定义HSV结构体
{
	int h;
	double s;
	double v;
};

bool IsEquals(double val1, double val2)
{
	return fabs(val1 - val2) < 0.001;
}

// 将RGB格式转换为HSV格式
void BGR2HSV(BGR &bgr, HSV &hsv)	
{
	double b, g, r;
	double h, s, v;
	double min, max;
	double delta;

	b = bgr.b / 255.0;
	g = bgr.g / 255.0;
	r = bgr.r / 255.0;

	if (r > g)
	{
		max = MAX(r, b);
		min = MIN(g, b);
	}
	else
	{
		max = MAX(g, b);
		min = MIN(r, b);
	}

	v = max;
	delta = max - min;

	if (IsEquals(max, 0))
	{
		s = 0.0;
	}
	else
	{
		s = delta / max;
	}

	if (max == min)
	{
		h = 0.0;
	}
	else
	{
		if (IsEquals(r, max) && g >= b)
		{
			h = 60 * (g - b) / delta + 0;
		}
		else if (IsEquals(r, max) && g < b)
		{
			h = 60 * (g - b) / delta + 360;
		}
		else if (IsEquals(g, max))
		{
			h = 60 * (b - r) / delta + 120;
		}
		else if (IsEquals(b, max))
		{
			h = 60 * (r - g) / delta + 240;
		}
	}

	hsv.h = (int)(h + 0.5);
	hsv.h = (hsv.h > 359) ? (hsv.h - 360) : hsv.h;
	hsv.h = (hsv.h < 0) ? (hsv.h + 360) : hsv.h;
	hsv.s = s;
	hsv.v = v;
}

// 填充算法(漫水天填充)
void fillHole(const Mat srcBw, Mat &dstBw)
{
	Size m_Size = srcBw.size();
	Mat Temp = Mat::zeros(m_Size.height + 2, m_Size.width + 2, srcBw.type());
	srcBw.copyTo(Temp(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)));

	cv::floodFill(Temp, Point(0, 0), Scalar(255));

	Mat cutImg;
	Temp(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)).copyTo(cutImg);

	dstBw = srcBw | (~cutImg);

}

//判断rect1与rect2是否有交集  
bool isInside(Rect rect1, Rect rect2)
{
	Rect t = rect1&rect2;
	if (rect1.area() > rect2.area())
	{
		return false;
	}
	else
	{
		if (t.area() != 0)
			return true;
	}
}

int main()
{
	// 载入检测图片
	Mat srcImg = imread("./src/3.jpg");
	if (srcImg.empty())
	{
		cout << "找不到相关图像,检查路径" << endl;
		return 0;
	}

	// 限定图像长宽
	int width = srcImg.cols;//图像宽度  
	int height = srcImg.rows;//图像高度
	if (width > 1920 || height >1080)
	{
		float factor = min((float)1920 / width, (float)1080 / height);
		resize(srcImg, srcImg, Size(factor*width, factor*height));
		width *= factor;
		height *= factor;
	}
	//cout << "width=" << width << ",height=" << height << endl;
	//imshow("srcImg", srcImg);
	//waitKey(0);

	// 第一步:分割红色颜色色块
	Mat matRgb = Mat::zeros(srcImg.size(), CV_8UC1);
	int x, y; //循环  
	for (y = 0; y < height; y++)
		for (x = 0; x < width; x++)
		{
			// 获取BGR值  
			BGR bgr;
			bgr.b = srcImg.at<Vec3b>(y, x)[0];
			bgr.g = srcImg.at<Vec3b>(y, x)[1];
			bgr.r = srcImg.at<Vec3b>(y, x)[2];

			HSV hsv;
			BGR2HSV(bgr, hsv); // bgr转hsv
			//红色范围  
			if ((hsv.h >= 135 * 2 && hsv.h <= 180 * 2 || hsv.h >= 0 && hsv.h <= 10 * 2) && hsv.s * 255 >= 16
				&& hsv.s * 255 <= 255 && hsv.v * 255 >= 46 && hsv.v * 255 <= 255)
			{
				matRgb.at<uchar>(y, x) = 255;
			}// if
		}// for

	// 第二步:去噪相关处理
	medianBlur(matRgb, matRgb, 3);// 中值滤波
	medianBlur(matRgb, matRgb, 5);// 中值滤波
	Mat element = getStructuringElement(MORPH_ELLIPSE, Size(2 * 1 + 1, 2 * 1 + 1), Point(1, 1));
	Mat element1 = getStructuringElement(MORPH_ELLIPSE, Size(2 * 3 + 1, 2 * 3 + 1), Point(3, 3));
	erode(matRgb, matRgb, element);//腐蚀    
	dilate(matRgb, matRgb, element1);//膨胀    

	// 第三步:填充
	fillHole(matRgb, matRgb);//填充   
	//imshow("fillHole", matRgb);
	//waitKey(0);

	// 第四步:找轮廓
	vector<vector<Point>>contours; //轮廓    
	vector<Vec4i> hierarchy;//分层    
	findContours(matRgb, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));//寻找轮廓    
	vector<vector<Point>> contours_poly(contours.size());  //近似后的轮廓点集    
	vector<Rect> boundRect(contours.size());  //包围点集的最小矩形vector

	// 第五步:画轮廓
	for (int i = 0; i < contours.size(); i++)
	{
		approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true); //对多边形曲线做适当近似,contours_poly[i]是输出的近似点集    
		boundRect[i] = boundingRect(Mat(contours_poly[i])); //计算并返回包围轮廓点集的最小矩形       
	}

	// 第六步:对提取出的轮廓进行去噪,筛选出交通标志
	Mat drawing = Mat::zeros(matRgb.size(), CV_8UC3);
	Mat imageContours1 = Mat::zeros(matRgb.size(), CV_8UC1); //最小外结圆画布
	vector<Mat> vec_roi;	// 存储筛选出的交通标志的图像信息
	vector<Rect> vec_rect;	// 存储交通标志相对于原图的roi区域
	for (int i = 0; i < contours.size(); i++)
	{
		Rect rect = boundRect[i];

		//1. 若轮廓矩形内部还包含着矩形,则将被包含的小矩形取消
		bool inside = false;
		for (int j = 0; j < contours.size(); j++)
		{
			Rect t = boundRect[j];
			if (rect == t)
				continue;
			else if (isInside(rect, t))
			{
				inside = true;
				break;
			}
		}// for
		if (inside)
			continue;

		//2.轮廓面积筛选      
		float Area = (float)rect.width * (float)rect.height;
		float dConArea = (float)contourArea(contours[i]);
		float dConLen = (float)arcLength(contours[i], 1);
		if (dConArea < 300)
			continue;

		//3.高宽比筛选
		float ratio = (float)rect.width / (float)rect.height;
		if (ratio > 1.3 || ratio < 0.4)
			continue;

		//4.圆形外观筛选
		Point2f center; float radius;
		minEnclosingCircle(contours[i], center, radius);
		double area_minEnclosingCircle = radius*radius*PI;
		double area_contour = contourArea(contours[i]);  //计算轮廓面积												
		if (area_contour < 0.5*area_minEnclosingCircle)
			continue;

		// 筛选完成,进行存储
		Mat roi = srcImg(Rect(boundRect[i].tl(), boundRect[i].br()));
		vec_roi.push_back(roi);
		vec_rect.push_back(Rect(boundRect[i].tl(), boundRect[i].br()));

	}

	// 第七步:载入模板的交通标志
	Mat template_srcimg = imread("./template/template.jpg");
	cvtColor(template_srcimg, template_srcimg, COLOR_BGR2GRAY); //图像灰度化

	//第八步:遍历所有交通标志,进行相似度匹配
	Mat gray_template, gray_roi;
	for (int i = 0; i < vec_roi.size(); i++)
	{
		// 创建一个模板副本
		template_srcimg.copyTo(gray_template);
		Mat tmp_roi = vec_roi[i].clone();
		//1. tmp_roi图像 resize为方形
		resize(tmp_roi, tmp_roi, cv::Size(min(tmp_roi.rows, tmp_roi.cols), min(tmp_roi.rows, tmp_roi.cols)));
		//2. tmp_roi图像灰度化
		cvtColor(tmp_roi, gray_roi, COLOR_BGR2GRAY); 
		//3. 与模板图像统一尺寸
		int w = gray_template.cols, h = gray_template.rows;
		resize(gray_roi, gray_roi, cv::Size(w, h));
		//4. 标记最大内接圆
		vector<vector<bool>> enclosingcircle_flag;
		Point center(0.5*w, 0.5*h);
		for (int col = 0; col < w; col++)
		{
			vector<bool> col_flag;
			for (int row = 0; row < h; row++)
			{
				bool flag;
				if (((col - center.x)*(col - center.x) + (row - center.y)*(row - center.y)) < center.x*center.x) // 内接圆内
					flag = true;
				else
					flag = false;
				col_flag.push_back(flag);
			}
			enclosingcircle_flag.push_back(col_flag);
		}
		//5.高斯滤波
		cv::GaussianBlur(gray_roi, gray_roi, cv::Size(7, 7), 3, 3);
		cv::GaussianBlur(gray_roi, gray_roi, cv::Size(5, 5), 3, 3);
		cv::GaussianBlur(gray_template, gray_template, cv::Size(7, 7), 3, 3);
		cv::GaussianBlur(gray_template, gray_template, cv::Size(5, 5), 3, 3);
		//6.二值化
		// 与图像的灰度值均值作为二值化的阈值
		int gray_mean1 = 0, gray_mean2 = 0;
		for (int x = 0; x < w; x++)
			for (int y = 0; y < h; y++) {
				gray_mean1 += gray_roi.at<uchar>(y, x);
				gray_mean2 += gray_template.at<uchar>(y, x);
			}
		gray_mean1 /= (w*h);
		gray_mean2 /= (w*h);
		threshold(gray_roi, gray_roi, gray_mean1, 255, cv::THRESH_BINARY_INV);
		threshold(gray_template, gray_template, gray_mean2, 255, cv::THRESH_BINARY_INV);
		//imshow("gray_roi.jpg", gray_roi);
		//imshow("gray_template.jpg", gray_template);
		//7. 相似度计算
		// 比较两个图255像素点的交集与并集的比值
		float jiaoji = 0, bingji = 0;
		for (int x = 0; x < w; x++)
			for (int y = 0; y < h; y++)
			{
				if (enclosingcircle_flag[x][y] == false)
					continue; // 不处于内接圆,跳过
				if (gray_roi.at<uchar>(y, x) == 255 && gray_template.at<uchar>(y, x) == 255)	//交集
					jiaoji++;
				if (gray_roi.at<uchar>(y, x) == 255 || gray_template.at<uchar>(y, x) == 255)	//并集
					bingji++;
			}
		float score = jiaoji / bingji;
		std::stringstream buf;
		buf.precision(3);//覆盖默认精度
		buf.setf(std::ios::fixed);//保留小数位
		buf << score;
		std::string str;
		str = buf.str();
		putText(srcImg, str, Point(vec_rect[i].x, vec_rect[i].y), FONT_HERSHEY_PLAIN, 2, Scalar(255, 255, 0), 2);
		//8. 相似度判断
		if (score > 0.7) // 判定通过
		{
			rectangle(srcImg, vec_rect[i], Scalar(255, 0, 0), 4, 8, 0); //相似度通过,画蓝框
		}
		else
		{
			rectangle(srcImg, vec_rect[i], Scalar(0, 0, 255), 4, 8, 0); //相似度不通过,画红框
		}
	}

	imshow("result.jpg", srcImg);//显示最终效果图
	waitKey(0);
	return 0;
}

srcImg 就是我们的待检测图像,效果如下:
在这里插入图片描述
template_srcimg 就是我们需要检测的目标路标,效果如下:
在这里插入图片描述
最终效果:
在这里插入图片描述

标签:roi,Mat,gray,路标,opencv,详解,hsv,template,Size
来源: https://blog.csdn.net/sazass/article/details/116407064