C++?OpenCV紅綠燈檢測Demo實現詳解
很久以來一直想實現紅綠燈檢測,今天它來了。
原理
OpenCV好強,能夠提取紅綠燈的輪廓,并根據顏色空間判斷紅綠,不依賴深度學習算法也能做到可用的效果/demo。
紅綠燈檢測的基本步驟如下:
- 輪廓檢測、計數
- red、green和light_out三種狀態(tài)
- 提取顏色空間,紅和綠
- 膨脹和腐蝕,去除噪點
- 判斷3種狀態(tài)
代碼實現
基于網絡上的代碼做復現的時候,遇到了opencv不同版本所出現的標識符未聲明問題,我這里是基于opencv4.5.4實現的,4.x的應該都可以運行。
創(chuàng)建trafficlight.h頭文件,將一些引用和全局變量放進來:
#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;
然后創(chuàng)建main.cpp,將主函數和功能函數加進來:
//下一步:如何調整視頻檢測框,防止誤檢
#include "trafficlight.h"
/*
1.輪廓檢測、計數
2.red、green和light_out三種狀態(tài)
3.提取顏色空間,紅和綠
4.膨脹和腐蝕,去除噪點
5.判斷3種狀態(tài)
*/
//主函數
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()];
//確定要跟蹤的區(qū)域
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()];
// 確定要跟蹤的區(qū)域
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;
}
//確定兩個矩形區(qū)域是否相交
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站視頻):

打包程序為exe
首先在VS的擴展和更新中安裝Installer的擴展:

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

添加項目輸出:

在主輸出這里創(chuàng)建快捷方式,然后移動到User’s Desktop文件夾下:

然后添加工程所需文件,把工程所需的數據文件和依賴庫都添加進來:

找依賴庫的方式可以用這個命令,然后搜索并添加進來:

最后,點擊生成,生成完成后,就可以安裝了:

安裝文件如下:

這樣打包出來的安裝程序在開發(fā)電腦上可以正常運行,但分發(fā)出去后其他電腦運行會閃退,我已經把所需的dll(opencv)都添加進來了,有大佬解釋一下嗎。
以上。
總結
到此這篇關于C++ OpenCV紅綠燈檢測Demo實現的文章就介紹到這了,更多相關C++ OpenCV紅綠燈檢測內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

