[opencv完整项目详解] 传统图像算法解决路标的检测和识别
2021/5/5 1:25:16
本文主要是介绍[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
就是我们需要检测的目标路标,效果如下:
最终效果:
这篇关于[opencv完整项目详解] 传统图像算法解决路标的检测和识别的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-29Elasticsearch慢查询日志配置
- 2024-05-29揭秘华为如此多成功项目的产品关键——Charter模板
- 2024-05-29海外IDC业务拓展的7大挑战
- 2024-05-29InLine Chat功能优化对标Github Copilot,CodeGeeX带来更高效、更直观的编程体验!
- 2024-05-29CodeGeeX 智能编程助手 6 项功能升级,在Visual Studio插件市场霸榜2周!
- 2024-05-29AutoMQ 生态集成 Apache Doris
- 2024-05-292024年IDC行业的深度挖掘:机遇、挑战与未来展望
- 2024-05-29五款扩展组件齐发 —— Volcano、Keda、Crane-scheduler 等,邀你体验
- 2024-05-29AutoMQ 对象存储数据高效组织的秘密: Compaction
- 2024-05-29活动预告|来 GIAC 大会听大数据降本利器:AutoMQ 基于云原生重新设计的 Kafka