前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】OpenCV:红绿灯检测介绍及实现示例

【C++】OpenCV:红绿灯检测介绍及实现示例

作者头像
DevFrank
发布2024-07-24 15:05:38
900
发布2024-07-24 15:05:38
举报
文章被收录于专栏:C++开发学习交流

1. 原理

OpenCV好强,能够提取红绿灯的轮廓,并根据颜色空间判断红绿,不依赖深度学习算法也能做到可用的效果/demo。

红绿灯检测的基本步骤如下:

  1. 轮廓检测、计数
  2. red、green和light_out三种状态
  3. 提取颜色空间,红和绿
  4. 膨胀和腐蚀,去除噪点
  5. 判断3种状态

2. 代码实现

基于网络上的代码做复现的时候,遇到了opencv不同版本所出现的标识符未声明问题,我这里是基于opencv4.5.4实现的,4.x的应该都可以运行。

20230617-ubuntu(opencv3)测试通过。

创建trafficlight.h头文件,将一些引用和全局变量放进来:

代码语言:javascript
复制
#pragma once

#include "opencv2/opencv.hpp"
#include "opencv2/imgproc.hpp"
#include <opencv2/imgproc/types_c.h>	//opencv3-4
#include <opencv2/imgproc/imgproc_c.h>	//出现很多未声明标识符的问题
//#include <windows.h>
#include <iostream>

using namespace std;
using namespace cv;

// 函数声明
int processImgR(Mat);
int processImgG(Mat);
bool isIntersected(Rect, Rect);
void detect(Mat& frame);

// 全局变量
bool isFirstDetectedR = true;
bool isFirstDetectedG = true;
Rect* lastTrackBoxR;
Rect* lastTrackBoxG;
int lastTrackNumR;
int lastTrackNumG;

然后创建main.cpp,将主函数和功能函数加进来:

代码语言:javascript
复制
//下一步:如何调整视频检测框,防止误检
#include "trafficlight.h"

/*
1.轮廓检测、计数
2.red、green和light_out三种状态
3.提取颜色空间,红和绿
4.膨胀和腐蚀,去除噪点
5.判断3种状态
*/

//主函数

int main()
{
	int redCount = 0;
	int greenCount = 0;

	Mat frame;
	Mat img;
	Mat imgYCrCb;
	Mat imgGreen;
	Mat imgRed;

	// 亮度参数
	double a = 0.3;
	double b = (1 - a) * 125;

	VideoCapture capture("traffic.mkv");//导入视频的路径/摄像头 0
	if (!capture.isOpened())
	{
		cout << "Start device failed!\n" << endl;//启动设备失败!
		return -1;
	}

	// 帧处理
	while (1)
	{
		capture >> frame;
		//调整亮度
		frame.convertTo(img, img.type(), a, b);

		//转换为YCrCb颜色空间
		cvtColor(img, imgYCrCb, CV_BGR2YCrCb);

		imgRed.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1);
		imgGreen.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1);

		//分解YCrCb的三个成分
		vector<Mat> planes;
		split(imgYCrCb, planes);
		// 遍历以根据Cr分量拆分红色和绿色
		MatIterator_<uchar> it_Cr = planes[1].begin<uchar>(),
			it_Cr_end = planes[1].end<uchar>();
		MatIterator_<uchar> it_Red = imgRed.begin<uchar>();
		MatIterator_<uchar> it_Green = imgGreen.begin<uchar>();

		for (; it_Cr != it_Cr_end; ++it_Cr, ++it_Red, ++it_Green)
		{
			// RED, 145<Cr<470 红色
			if (*it_Cr > 145 && *it_Cr < 470)
				*it_Red = 255;
			else
				*it_Red = 0;

			// GREEN 95<Cr<110 绿色
			if (*it_Cr > 95 && *it_Cr < 110)
				*it_Green = 255;
			else
				*it_Green = 0;
		}

		//膨胀和腐蚀
		dilate(imgRed, imgRed, Mat(15, 15, CV_8UC1), Point(-1, -1));
		erode(imgRed, imgRed, Mat(1, 1, CV_8UC1), Point(-1, -1));
		dilate(imgGreen, imgGreen, Mat(15, 15, CV_8UC1), Point(-1, -1));
		erode(imgGreen, imgGreen, Mat(1, 1, CV_8UC1), Point(-1, -1));

		redCount = processImgR(imgRed);
		greenCount = processImgG(imgGreen);
		cout << "red:" << redCount << ";  " << "green:" << greenCount << endl;

		//条件判断
		if (redCount == 0 && greenCount == 0)
		{
			cv::putText(frame, "lights out", Point(40, 150), cv::FONT_HERSHEY_SIMPLEX, 2, cv::Scalar(255, 255, 255), 8, 8, 0);
		}
		else if (redCount > greenCount)
		{
			cv::putText(frame, "red light", Point(40, 150), cv::FONT_HERSHEY_SIMPLEX, 2, cv::Scalar(0, 0, 255), 8, 8, 0);
		}
		else {
			cv::putText(frame, "green light", Point(40, 150), cv::FONT_HERSHEY_SIMPLEX, 2, cv::Scalar(0, 255, 0), 8, 8, 0);
		}

		imshow("video", frame);
		//imshow("Red", imgRed);
		//imshow("Green", imgGreen);

		// Handle with the keyboard input
		if (waitKey(20) == 'q')
			break;
	}

	return 0;
}

//轮廓处理函数:红
int processImgR(Mat src)
{
	Mat tmp;

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	vector<Point> hull;

	CvPoint2D32f tempNode;
	CvMemStorage* storage = cvCreateMemStorage();
	CvSeq* pointSeq = cvCreateSeq(CV_32FC2, sizeof(CvSeq), sizeof(CvPoint2D32f), storage);

	Rect* trackBox;
	Rect* result;
	int resultNum = 0;

	int area = 0;
	src.copyTo(tmp);

	//提取轮廓
	findContours(tmp, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

	if (contours.size() > 0)
	{
		trackBox = new Rect[contours.size()];
		result = new Rect[contours.size()];

		//确定要跟踪的区域
		for (int i = 0; i < contours.size(); i++)
		{
			cvClearSeq(pointSeq);
			// 获取凸包的点集
			convexHull(Mat(contours[i]), hull, true);
			int hullcount = (int)hull.size();
			// 凸包的保存点
			for (int j = 0; j < hullcount - 1; j++)
			{
				tempNode.x = hull[j].x;
				tempNode.y = hull[j].y;
				cvSeqPush(pointSeq, &tempNode);
			}

			trackBox[i] = cvBoundingRect(pointSeq);
		}

		if (isFirstDetectedR)
		{
			lastTrackBoxR = new Rect[contours.size()];
			for (int i = 0; i < contours.size(); i++)
				lastTrackBoxR[i] = trackBox[i];
			lastTrackNumR = contours.size();
			isFirstDetectedR = false;
		}
		else
		{
			for (int i = 0; i < contours.size(); i++)
			{
				for (int j = 0; j < lastTrackNumR; j++)
				{
					if (isIntersected(trackBox[i], lastTrackBoxR[j]))
					{
						result[resultNum] = trackBox[i];
						break;
					}
				}
				resultNum++;
			}
			delete[] lastTrackBoxR;
			lastTrackBoxR = new Rect[contours.size()];
			for (int i = 0; i < contours.size(); i++)
			{
				lastTrackBoxR[i] = trackBox[i];
			}
			lastTrackNumR = contours.size();
		}

		delete[] trackBox;
	}
	else
	{
		isFirstDetectedR = true;
		result = NULL;
	}
	cvReleaseMemStorage(&storage);

	if (result != NULL)
	{
		for (int i = 0; i < resultNum; i++)
		{
			area += result[i].area();
		}
	}
	delete[] result;

	return area;
}

//轮廓处理函数:绿
int processImgG(Mat src)
{
	Mat tmp;

	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	vector< Point > hull;

	CvPoint2D32f tempNode;
	CvMemStorage* storage = cvCreateMemStorage();
	CvSeq* pointSeq = cvCreateSeq(CV_32FC2, sizeof(CvSeq), sizeof(CvPoint2D32f), storage);

	Rect* trackBox;
	Rect* result;
	int resultNum = 0;

	int area = 0;

	src.copyTo(tmp);
	//提取轮廓
	findContours(tmp, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

	if (contours.size() > 0)
	{
		trackBox = new Rect[contours.size()];
		result = new Rect[contours.size()];

		// 确定要跟踪的区域
		for (int i = 0; i < contours.size(); i++)
		{
			cvClearSeq(pointSeq);
			// 获取凸包的点集
			convexHull(Mat(contours[i]), hull, true);
			int hullcount = (int)hull.size();
			// 保存凸包的点
			for (int j = 0; j < hullcount - 1; j++)
			{
				tempNode.x = hull[j].x;
				tempNode.y = hull[j].y;
				cvSeqPush(pointSeq, &tempNode);
			}

			trackBox[i] = cvBoundingRect(pointSeq);
		}

		if (isFirstDetectedG)
		{
			lastTrackBoxG = new Rect[contours.size()];
			for (int i = 0; i < contours.size(); i++)
				lastTrackBoxG[i] = trackBox[i];
			lastTrackNumG = contours.size();
			isFirstDetectedG = false;
		}
		else
		{
			for (int i = 0; i < contours.size(); i++)
			{
				for (int j = 0; j < lastTrackNumG; j++)
				{
					if (isIntersected(trackBox[i], lastTrackBoxG[j]))
					{
						result[resultNum] = trackBox[i];
						break;
					}
				}
				resultNum++;
			}
			delete[] lastTrackBoxG;
			lastTrackBoxG = new Rect[contours.size()];
			for (int i = 0; i < contours.size(); i++)
			{
				lastTrackBoxG[i] = trackBox[i];
			}
			lastTrackNumG = contours.size();
		}

		delete[] trackBox;
	}
	else
	{
		isFirstDetectedG = true;
		result = NULL;
	}
	cvReleaseMemStorage(&storage);

	if (result != NULL)
	{
		for (int i = 0; i < resultNum; i++)
		{
			area += result[i].area();
		}
	}
	delete[] result;

	return area;
}

//确定两个矩形区域是否相交
bool isIntersected(Rect r1, Rect r2)
{
	int minX = max(r1.x, r2.x);
	int minY = max(r1.y, r2.y);
	int maxX = min(r1.x + r1.width, r2.x + r2.width);
	int maxY = min(r1.y + r1.height, r2.y + r2.height);

	//判断是否相交
	if (minX < maxX && minY < maxY)
		return true;
	else
		return false;
}

运行结果如下(b站视频):

3. 打包程序为exe

首先在VS的扩展和更新中安装Installer的扩展:

然后在解决方案下新建setup工程:

添加项目输出:

在这里插入图片描述
在这里插入图片描述

在主输出这里创建快捷方式,然后移动到User’s Desktop文件夹下:

然后添加工程所需文件,把工程所需的数据文件和依赖库都添加进来:

在这里插入图片描述
在这里插入图片描述

找依赖库的方式可以用这个命令,然后搜索并添加进来:

在这里插入图片描述
在这里插入图片描述

最后,点击生成,生成完成后,就可以安装了:

在这里插入图片描述
在这里插入图片描述

安装文件如下:

在这里插入图片描述
在这里插入图片描述

这样打包出来的安装程序就可以正常运行了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 原理
  • 2. 代码实现
  • 3. 打包程序为exe
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档