【OpenCV實戰】報警燈檢測裝置(像素操作)

目錄

前言

一、項目要求

二、項目分析

1、獲取圖像

2、選擇區域

3、區域像素檢測

4、報警

三、實戰

1、獲取圖像

2、選擇區域

3、區域像素檢測

4、報警

5、全部代碼


前言

大學時候,曾經暑假在學校值班,當時一個學長在準備考研,學長是我在計算機視覺的引路人,當時我們要做一件事,就是監控火警燈,因爲火警報警器出故障了,當時學長跟我說,可以做一個自動識別然後進行報警的裝置。這樣就不用每時每刻都盯着它,就可以把精力放在其他事情上。

這個項目化簡一下,我們就可以變成如下這個項目,用我們最近學習的讀寫像素就可以完成啦。

一、項目要求

下面是火警等一系列監控界面,監控正常情況如下:

火警只有在有地方的煙感設備監測到有煙出現,纔會亮燈並且報警。

當監控出現問題時,故障就會亮燈。

但是監控器出故障之後:

火警預警燈就會定期閃亮,並且沒有報警;

故障燈一直常亮狀態;

爲了防患於未然,學校的要求是,只要有火警燈亮起,就必須到報警地查看情況,如果沒有問題,回到監控室,手動關閉火警燈。

現在我們要做的就是通過攝像頭自動監控火警燈,如果亮起,發出警報

火警燈亮起
火警燈未亮

二、項目分析

我們目前學習了幾個最基本的內容:

圖像輸入、輸出、保存

Mat類

基本數據類型

像素基本操作(獲取像素指針、控制像素範圍、讀寫像素)

我們就要用上面的來完成該項目。

我們分析一下這個項目,主要分爲如下步驟:

1、獲取圖像

原項目是通過視頻流獲取圖像,現在我們沒有具體得環境,就只能用圖片,檢測原理是一樣的。我們要先獲取圖像,保證圖像能夠正確讀取,才能完成後續操作。

2、選擇區域

遍歷所有圖像的運算速度是非常慢的,爲了加快運算效率,我們要選擇縮小區域進行檢測。所以我們要選擇如下區域:

因爲只有上面的幾個基本內容,那我們就沒有辦法使用更多的東西進行定位,我們定位的方式就是通過反覆檢測,然後找到一個合適的區域:

(1)儘可能只包含火警區域;

(2)不受攝像頭抖動影響,所以攝像頭輕微抖動,火警燈依然在範圍內;

3、區域像素檢測

獲取火警燈位置的像素,並根據像素值選定亮燈及滅燈像素範圍。

但是我們發現,滅燈狀態,和火警外面的圓角矩形邊框的顏色是類似的,所以檢測滅燈容易出現誤報。而亮燈的燈光像素應該是很明顯的,再加上燈只有燈亮和燈滅兩種狀態,所以我們只需要檢測亮燈的像素範圍就好。在像素範圍內,就說明亮燈,不在像素範圍內,就說明滅燈。

當我們確定了亮燈的像素範圍後,我們就需要實時監測我們選擇的區域像素,並判斷是否有像素在我們上面規定好的範圍之內。

4、報警

一旦出現有大量的像素點滿足在範圍之內,那我們就報警。所以我們要設定一個閾值,達到多少時,我們認爲是火警燈亮。

爲什麼我們要檢測到大量呢?

因爲根據環境光線不同,一旦出現反光,也會導致出現部分點滿足範圍。

 

三、實戰

1、獲取圖像

首先我們要獲取圖像。這個用到我們最開始的圖像的讀入,理論上我們是要使用攝像頭,然後獲取幀,在這裏,我們就直接讀入圖片吧!這並不影響後續的操作,也沒有改變檢測原理。代碼如下:

	Mat src = imread("./image/1.png"); //1.png 是亮燈, 2.png 是滅燈
	if (!src.data)
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}

2、選擇區域

然後需要我們獲取區域,最直接的方法就是憑感覺去設定,然後不斷調整找到一個合適的區域。

當然如果我們學的更多的話,我們可以用到更多的功能去獲得區域,比如當時我嘗試瞭如下方式:

(1)給那個區域貼上紅紙,檢測紅紙;

(2)使用文字識別模塊,檢測火警文字,然後獲取區域。

現在我們學的內容比較少,就用最簡單、最直接的方式吧!

代碼如下:

	//不斷調整Rect,找到對於我的比較合適爲Rect(70, 60, 150, 60)。
	Mat src_ROI = Mat(src, Rect(70, 60, 150, 60));
	imshow("src_ROI", src_ROI);

區域如下:

3、區域像素檢測

獲取火警燈位置的像素,並根據像素值選定亮燈及滅燈像素範圍。

我們要檢測的區域爲:

這是一個22×22的正方形區域,因爲亮燈區域是一個圓形區域,所以,我們考慮準確度,那我們在檢測像素,應該是檢測到圖像中心距離爲11像素範圍內的圖像:

	int max_B = 0, max_G = 0, max_R = 0;
	int min_B = 255, min_G = 255, min_R = 255;

	//半徑爲9的圓區域像素
	for (int i = 0; i < light.rows; i++)
	{
		for (int j = 0; j < light.cols; j++)
		{
			if ((i - 11)*(i - 9) + (j - 9)*(j - 9) <= 81) {
				Vec3b BGR = light.at<Vec3b>(i, j);
				int B = BGR.val[0];
				int G = BGR.val[1];
				int R = BGR.val[2];
				cout << "[" << B << ", " << G << ", " << R << "]\t";

				if (B > max_B) max_B = B;
				else if (B < min_B) min_B = B;
				if (G > max_G) max_G = G;
				else if (G < min_G) min_B = G;
				if (R > max_R) max_R = R;
				else if (R < min_R) min_R = R;
			}			
		}
		cout << endl << endl;
	}
	cout << "B: [" << min_B << ", " << max_B << "]" << endl;
	cout << "G: [" << min_G << ", " << max_G << "]" << endl;
	cout << "R: [" << min_R << ", " << max_R << "]" << endl;

檢測的結果如下:

如果想讓精度更高,我們可以縮小範圍,比如到圖像中心距離爲10那麼檢測到的結果如下:

我們可以根據上面我們獲得的範圍,來選擇我們的範圍,比如我們的像素範圍可以是:

B : ≥125;G : = 155;R : ≥ 228;

4、報警

一個半徑爲10的圓形中間的像素點的個數肯定大於邊長爲10的正方形中間的像素點的個數。

邊長爲10的正方形有100個像素點。那我們的閾值,可以設爲100,只要我們檢測到滿足上面的範圍的像素點的個數大於100時,就認爲火警燈亮起。爲了查看哪些點被檢測出來,我們將這些點的像素值修改爲黑色。

火警燈亮起,我們給出提示。

	int num = 0;
	//按列遍歷,效率更高
	for (int i = 0; i < src_ROI.cols; i++)
	{
		for (int j = 0; j < src_ROI.rows; j++)
		{
			BGR = src_ROI.at<Vec3b>(j, i);
			if (BGR.val[0] >= min_B && BGR.val[1] >= min_G && BGR.val[2] >= min_R) {
				src_ROI.at<Vec3b>(j, i)[0] = 0;
				src_ROI.at<Vec3b>(j, i)[1] = 0;
				src_ROI.at<Vec3b>(j, i)[2] = 0;
				num++;
				//cout << "num = " << num << endl;
			}

		}
	}
	if (num > 100) {
		cout << "親,疑似火警,請親臨現場查看!" << endl;
	}
	imshow("new_src_ROI", src_ROI);

結果如下:

5、全部代碼

全部代碼如下:

/*
	作者:水亦心
	內容:core-像素基本操作實戰之報警燈檢測
	時間:2020年5月20日
*/
#define INPUT_TITLE "input image"

#include<iostream>
#include<opencv2\opencv.hpp>

using namespace std;
using namespace cv;


int main() {
	//////////////////////////////// 獲取圖像 ////////////////////////////////

	Mat src = imread("./image/1.png"); //1.png 是亮燈, 2.png 是滅燈
	if (!src.data)
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}
	
	//////////////////////////////// 選擇區域 ////////////////////////////////

	//不斷調整Rect,找到對於我的比較合適爲Rect(70, 60, 150, 60)。
	Mat src_ROI = Mat(src, Rect(70, 60, 150, 60));
	imshow("src_ROI", src_ROI);

	////////////////////////////// 區域像素檢測 //////////////////////////////
	Mat light = imread("./image/3.png"); //光區域
	if (!light.data)
	{
		cout << "ERROR : could not load light image.\n";
		return -1;
	}
	int max_B = 0, max_G = 0, max_R = 0;
	int min_B = 255, min_G = 255, min_R = 255;
	int B, G, R;
	Vec3b BGR;
	//半徑爲9的圓區域像素
	int k = 0;
	for (int i = 0; i < light.rows; i++)
	{
		for (int j = 0; j < light.cols; j++)
		{
			if ((i - 11)*(i - 11) + (j - 11)*(j - 11) <= 100) {
				BGR = light.at<Vec3b>(i, j);
				B = BGR.val[0];
				G = BGR.val[1];
				R = BGR.val[2];
				//cout << "[" << B << ", " << G << ", " << R << "]\t";

				//light.at<Vec3b>(i, j)[0] = 0;
				//light.at<Vec3b>(i, j)[1] = 0;
				//light.at<Vec3b>(i, j)[2] = 0;
				//imshow("new light", light);

				if (B > max_B) max_B = B;
				if (B < min_B) min_B = B;
				if (G > max_G) max_G = G;
				if (G < min_G) min_G = G;
				if (R > max_R) max_R = R;
				if (R < min_R) min_R = R;
			}			
		}
		//cout << endl << endl;
	}
	cout << "B: [" << min_B << ", " << max_B << "]" << endl;
	cout << "G: [" << min_G << ", " << max_G << "]" << endl;
	cout << "R: [" << min_R << ", " << max_R << "]" << endl;
	cout << endl;


	//Mat test = Mat(100, 100, CV_8UC3, Scalar(min_B, min_G, min_R));
	//imshow("test", test);
	//////////////////////////////// 監測及報警 ///////////////////////////////
		
	int num = 0;
	//按列遍歷,效率更高
	for (int i = 0; i < src_ROI.cols; i++)
	{
		for (int j = 0; j < src_ROI.rows; j++)
		{
			BGR = src_ROI.at<Vec3b>(j, i);
			if (BGR.val[0] >= min_B && BGR.val[1] >= min_G && BGR.val[2] >= min_R) {
				src_ROI.at<Vec3b>(j, i)[0] = 0;
				src_ROI.at<Vec3b>(j, i)[1] = 0;
				src_ROI.at<Vec3b>(j, i)[2] = 0;
				num++;
				//cout << "num = " << num << endl;
			}

		}
	}
	if (num > 100) {
		cout << "親,疑似火警,請親臨現場查看!" << endl;
	}
	imshow("new_src_ROI", src_ROI);

	waitKey(0);
	return 0;
}

如果我們更換一下圖片,我們使用未亮燈圖片,沒有提示,也沒有任何改變:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章