C++?OpenCV紅綠燈檢測(cè)Demo實(shí)現(xiàn)詳解
很久以來(lái)一直想實(shí)現(xiàn)紅綠燈檢測(cè),今天它來(lái)了。
原理
OpenCV好強(qiáng),能夠提取紅綠燈的輪廓,并根據(jù)顏色空間判斷紅綠,不依賴深度學(xué)習(xí)算法也能做到可用的效果/demo。
紅綠燈檢測(cè)的基本步驟如下:
- 輪廓檢測(cè)、計(jì)數(shù)
- red、green和light_out三種狀態(tài)
- 提取顏色空間,紅和綠
- 膨脹和腐蝕,去除噪點(diǎn)
- 判斷3種狀態(tài)
代碼實(shí)現(xiàn)
基于網(wǎng)絡(luò)上的代碼做復(fù)現(xiàn)的時(shí)候,遇到了opencv不同版本所出現(xiàn)的標(biāo)識(shí)符未聲明問(wèn)題,我這里是基于opencv4.5.4
實(shí)現(xiàn)的,4.x的應(yīng)該都可以運(yùn)行。
創(chuàng)建trafficlight.h
頭文件,將一些引用和全局變量放進(jìn)來(lái):
#pragma once #include "opencv2/opencv.hpp" #include "opencv2/imgproc.hpp" #include <opencv2/imgproc/types_c.h> //opencv3-4 #include <opencv2/imgproc/imgproc_c.h> //出現(xiàn)很多未聲明標(biāo)識(shí)符的問(wèn)題 #include <windows.h> #include <iostream> using namespace std; using namespace cv; // 函數(shù)聲明 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
,將主函數(shù)和功能函數(shù)加進(jìn)來(lái):
//下一步:如何調(diào)整視頻檢測(cè)框,防止誤檢 #include "trafficlight.h" /* 1.輪廓檢測(cè)、計(jì)數(shù) 2.red、green和light_out三種狀態(tài) 3.提取顏色空間,紅和綠 4.膨脹和腐蝕,去除噪點(diǎn) 5.判斷3種狀態(tài) */ //主函數(shù) int main() { int redCount = 0; int greenCount = 0; Mat frame; Mat img; Mat imgYCrCb; Mat imgGreen; Mat imgRed; // 亮度參數(shù) double a = 0.3; double b = (1 - a) * 125; VideoCapture capture("traffic.mkv");//導(dǎo)入視頻的路徑/攝像頭 0 if (!capture.isOpened()) { cout << "Start device failed!\n" << endl;//啟動(dòng)設(shè)備失敗! return -1; } // 幀處理 while (1) { capture >> frame; //調(diào)整亮度 frame.convertTo(img, img.type(), a, b); //轉(zhuǎn)換為YCrCb顏色空間 cvtColor(img, imgYCrCb, CV_BGR2YCrCb); imgRed.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1); imgGreen.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1); //分解YCrCb的三個(gè)成分 vector<Mat> planes; split(imgYCrCb, planes); // 遍歷以根據(jù)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; } //輪廓處理函數(shù):紅 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); // 獲取凸包的點(diǎn)集 convexHull(Mat(contours[i]), hull, true); int hullcount = (int)hull.size(); // 凸包的保存點(diǎn) 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; } //輪廓處理函數(shù):綠 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); // 獲取凸包的點(diǎn)集 convexHull(Mat(contours[i]), hull, true); int hullcount = (int)hull.size(); // 保存凸包的點(diǎn) 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; } //確定兩個(gè)矩形區(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; }
運(yùn)行結(jié)果如下(b站視頻):
打包程序?yàn)閑xe
首先在VS的擴(kuò)展和更新中安裝Installer的擴(kuò)展:
然后在解決方案下新建setup工程:
添加項(xiàng)目輸出:
在主輸出這里創(chuàng)建快捷方式,然后移動(dòng)到User’s Desktop文件夾下:
然后添加工程所需文件,把工程所需的數(shù)據(jù)文件和依賴庫(kù)都添加進(jìn)來(lái):
找依賴庫(kù)的方式可以用這個(gè)命令,然后搜索并添加進(jìn)來(lái):
最后,點(diǎn)擊生成,生成完成后,就可以安裝了:
安裝文件如下:
這樣打包出來(lái)的安裝程序在開(kāi)發(fā)電腦上可以正常運(yùn)行,但分發(fā)出去后其他電腦運(yùn)行會(huì)閃退,我已經(jīng)把所需的dll(opencv)都添加進(jìn)來(lái)了,有大佬解釋一下嗎。
以上。
總結(jié)
到此這篇關(guān)于C++ OpenCV紅綠燈檢測(cè)Demo實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ OpenCV紅綠燈檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于c++ ege圖形庫(kù)實(shí)現(xiàn)五子棋游戲
這篇文章主要為大家詳細(xì)介紹了基于c++ ege圖形庫(kù)實(shí)現(xiàn)五子棋游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12C與C++動(dòng)態(tài)分配二維數(shù)組的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇C與C++動(dòng)態(tài)分配二維數(shù)組的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12C++解決輸出鏈表中倒數(shù)k個(gè)結(jié)點(diǎn)的問(wèn)題
這篇文章主要給大家介紹了關(guān)于如何利用C++解決輸出鏈表中倒數(shù)k個(gè)結(jié)點(diǎn)的問(wèn)題,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-12-12詳解C 語(yǔ)言項(xiàng)目中.h文件和.c文件的關(guān)系
這篇文章主要介紹了詳解C 語(yǔ)言項(xiàng)目中.h文件和.c文件的關(guān)系的相關(guān)資料,需要的朋友可以參考下2017-05-05