根据三基色原理,用基色光单位来表示光的量,则在RGB颜色空间,任意色光F都可以用R、G、B三色不同分量的相加混合而成:
F=r[R]+g[G]+b[B]
当三基色分量都为0(最弱)时混合为黑色光;当三基色分量都为k(最强)时混合为白。改变了F的坐标值,也即改变了F的色值。
采用三维空间来进行描述(如图1),把三种成分的数值当做欧几里得空间中普通笛卡尔坐标系的坐标值。在RGB模型中使用0到1之间的非负数作为立方体的坐标值,将原点(0,0,0)作为黑色,强度值沿坐标轴方向递增到达位于对角线(1,1,1)处的白色。
图 1 RGB三维空间
一个RGB组合(r,g,b)表示代表一个给定颜色的点在立方体内部、表面或者边上的三维坐标。这种表示方法使得在计算两个颜色相近程度时只需简单计算它们之间的距离:距离越短颜色越接近。
眼的视网膜上有两类感光器:锥状体和杆状体。锥状体主要位于视网膜的中间部分,称之为中央凹,且对颜色高度敏感,称为白昼视觉或亮视觉;杆状体分布面积较大,用来给出视野内的一般的总体图像,没有彩色感觉,而对低照明度敏感,称为微光视觉或暗视觉。由于锥状体对红、绿、蓝三种颜色的光很敏感,因此一般用于人眼观看的颜色模型是RGB模型。一般来说,无论是在网上下载的图片或视频,还是从摄像机得来的录像,都是RGB模型。
对于普通火焰来说,它的红色分量和绿色分量会很大,并且绿色分量会大于蓝色分量,所以我们写出如下不等式:
R > RedThreshold
R>G>B
其中,RedThreshold为红色分量的阈值。
经过这两个不等式的判断,我们可以通过二值图的方法得到火焰的外形,如图2所示。
(b)二值化后图像
图2 用RGB判据对图片二值化
首先导入一张图片,然后对该图片进行RGB颜色判据,从而二值化,然后对二值化后的图像进行框选选出火焰部分。
首先先要检测函数导入的两个参量是否有错,使用if语句进行判断。然后使用image_input.at的方法将RGB三个通道分开。
其中要注意的是RGB三个通道的顺序,在程序中B是第一个,R是最后一个。
然后,把分离出的R、G、B分量进行RGB颜色判据判断,从而二值化。最后输出原始图像和二值化图像。
首先保存二值化后的整个火焰的轮廓,然后使用迭代器对每个点进行判断是否是火焰,判断为是则框选,最后输出图像。
void FireDetection(Mat &image_input, Mat &image_output)
{
image_output = Mat::zeros(image_input.size(), CV_8UC1); //初始化output矩阵
if (image_input.empty()) {
printf("could not load image...please try again!/n ");
}
if (image_output.empty()||image_output.channels()!= 1) {
printf("could not initialize output...please try again!/n ");
}
/*初始化程序报错*/
for (int i = 0; i < image_input.rows; i++) {
for (int j = 0; j < image_input.cols; j++) {
float R, G, B;
B = image_input.at<uchar>(i, image_input.channels()*j + 0);
G = image_input.at<uchar>(i, image_input.channels()*j + 1);
R = image_input.at<uchar>(i, image_input.channels()*j + 2);
int minRGB = min(min(R, G), B);
double S = 1 - 3.0*minRGB / (R + G + B);
int RedThreshold = 115;
if (R > RedThreshold && R > G&&G > B)
{
image_output.at<uchar>(i, j) = 255;
}
else
{
image_output.at<uchar>(i, j) = 0;
}
}
}
namedWindow("original image", WINDOW_AUTOSIZE);
imshow("original image", image_input);
namedWindow("binary image", WINDOW_AUTOSIZE);
imshow("binary image", image_output);
DrawFire(image_input, image_output);
}
void DrawFire(Mat &inputImg, Mat foreImg)
{
vector<vector<Point>> contours_set;//保存轮廓提取后的点集及拓扑关系
findContours(foreImg, contours_set, RETR_EXTERNAL, CHAIN_APPROX_NONE);
Mat result0;
Scalar holeColor;
Scalar externalColor;
vector<vector<Point>>::iterator iter = contours_set.begin();
for (; iter != contours_set.end(); ) //迭代器循环
{
Rect rect = boundingRect(*iter);
/* float radius;
Point2f center;
minEnclosingCircle(*iter, center, radius);*/
if (rect.area()> 0)
{
rectangle(inputImg, rect, Scalar(0, 255, 0)); //scalar表示是什么颜色去框选
++iter;
}
else
{
iter = contours_set.erase(iter);
}
}
imshow("Detect Fire", inputImg);
}