天天看點

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

目錄

​​前言​​

​​一、項目要求​​

​​二、項目分析​​

​​1、擷取圖像​​

​​2、選擇區域​​

​​3、區域像素檢測​​

​​4、報警​​

​​三、實戰​​

​​1、擷取圖像​​

​​2、選擇區域​​

​​3、區域像素檢測​​

​​4、報警​​

​​5、全部代碼​​

前言

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

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

一、項目要求

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

火警隻有在有地方的煙感裝置監測到有煙出現,才會亮燈并且報警。

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

但是監控器出故障之後:

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

故障燈一直常亮狀态;

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

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

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

火警燈亮起

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

火警燈未亮

二、項目分析

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

圖像輸入、輸出、儲存

Mat類

基本資料類型

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

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

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

1、擷取圖像

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

2、選擇區域

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

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

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

(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);      

區域如下:

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

3、區域像素檢測

擷取火警燈位置的像素,并根據像素值標明亮燈及滅燈像素範圍。

我們要檢測的區域為:

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

這是一個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;      

檢測的結果如下:

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

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

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

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

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);      

結果如下:

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

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

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

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