【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;
}

如果我们更换一下图片,我们使用未亮灯图片,没有提示,也没有任何改变:

 

 

  • 18
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值