基于OpenCV的火焰检测(三)——HSI颜色判据

来源:互联网 发布:武汉淘宝培训 编辑:程序博客网 时间:2024/05/16 11:28
    上文向大家介绍了如何用最简单的RGB判据来初步提取火焰区域,现在我要给大家分享的是一种更加直观的判据——HSI判据。    为什么说HSI判据是更加直观的判据呢?老规矩,先介绍一下HSI色彩模型:    HSI颜色模型用H、S、I三参数描述颜色特性,其中:    H表示颜色的色调,它表示人的感官对不同颜色的感受,如红色、绿色、蓝色等,它也可表示一定范围的颜色,如暖色、冷色等。    H的单位是°,代表与红轴的角度。

这里写图片描述

    S表示颜色的饱和度,纯光谱色是完全饱和的,加入白光会稀释饱和度。饱和度越大,颜色看起来就会越鲜艳。    I对应成像亮度和图像灰度。    HSI模型的建立基于两个重要的事实: ① I分量与图像的彩色信息无关;② H和S分量与人感受颜色的方式是紧密相联的。    这些特点使得HSI模型非常适合彩色特性检测与分析。    对比一下RGB和HSI模型:

这里写图片描述

    得出由RGB模型转化为HSI模型的公式:

这里写图片描述

    假设R、G、B分量已经归一化到[0,1],那么求出来的S分量和I分量的值也会被归一化到[0,1]。当S分量为0的时候,对应的H分量也应该为0。    用OpenCV1.0可以根据上面的公式和条件写出模型转化函数的代码:
int cvBGR2HSI(IplImage*img_bgr, IplImage*img_hsi){    if (img_bgr == NULL || img_hsi == NULL){        printf("func cvBGR2HSI Error:\n");        printf("img_bgr == NULL || img_hsi == NULL\n");        return -1;    }    if (img_bgr->nChannels != 3 || img_hsi->nChannels != 3){        printf("func cvBGR2HSI Error:\n");        printf("img_bgr->nChannels != 3 || img_hsi->nChannels != 3\n");        return -1;    }    double eps = 1.111e-016;    double pi = 3.1416;    double num = 0;    double den = 0;    double theta = 0;    double R, G, B, H, S, I;    for (int i = 0; i < img_bgr->height; i++){        uchar*ptr1 = (uchar*)(img_bgr->imageData + i*img_bgr->widthStep);        uchar*ptr2 = (uchar*)(img_hsi->imageData + i*img_hsi->widthStep);        for (int j = 0; j < img_bgr->width; j++){            R = double(ptr1[3 * j + 2]) / 255.0;            G = double(ptr1[3 * j + 1]) / 255.0;            B = double(ptr1[3 * j + 0]) / 255.0;            num = 0.5*((R - G) + (R - B));            den = sqrt((R - G)*(R - G) + (R - B)*(G - B));            theta = acos(num / (den + eps));            H = theta;            if (B>G){                H = 2 * pi - H;            }            H = H / (2 * pi);            num = min(min(R, G), B);            den = R + G + B;            if (den < eps){                den = eps;            }            S = 1 - 3 * num / den;            if (S < eps){                H = 0;            }            I = (R + G + B) / 3.0;            ptr2[3 * j + 0] = H * 360;            ptr2[3 * j + 1] = S * 255;            ptr2[3 * j + 2] = I * 255;        }    }    return 0;}
    在代码中要注意的是在OpenCV中RGB三通道的排列顺序:先存放B分量,再存放G分量,最后才是R分量,这与MATLAB里面的规则是不同的。    有了模型转化函数之后,我们就可以在HSI色彩模型内使用HSI判据了。HSI判据很简单,也很直观,它的规则是每个分量设定两个阈值,满足阈值条件的置1,不满足的置0。具体表达式如下:                        H_min < H < H_max  AND                        S_min < S < S_max  AND                        I_min < I < I_max    在OpenCV1.0中可以轻松写出上面的代码:
int cvHSI_CHK(IplImage*img_hsi, IplImage*hsi_chk, int H_min, int H_max, int S_min, int S_max, int I_min, int I_max){    if (img_hsi == NULL || hsi_chk == NULL){        printf("func cvHSI_CHK Error:\n");        printf("img_hsi == NULL || hsi_chk == NULL)\n");        return -1;    }    if (img_hsi->nChannels != 3 || hsi_chk->nChannels != 1){        printf("func cvHSI_CHK Error:\n");        printf("img_hsi->nChannels != 3 || hsi_chk->nChannels != 1)\n");        return -1;    }    CvSize size = cvGetSize(img_hsi);    IplImage*H = cvCreateImage(size, 8, 1);    IplImage*S = cvCreateImage(size, 8, 1);    IplImage*I = cvCreateImage(size, 8, 1);    IplImage*mask = cvCreateImage(size, 8, 1);    cvSplit(img_hsi, H, S, I, NULL);    cvCmpS(H, H_min, mask, CV_CMP_GE);    cvCmpS(H, H_max, H, CV_CMP_LE);    cvMul(H, mask, H);    cvCmpS(S, S_min, mask, CV_CMP_GE);    cvCmpS(S, S_max, S, CV_CMP_LE);    cvMul(S, mask, S);    cvCmpS(I, I_min, mask, CV_CMP_GE);    cvCmpS(I, I_max, I, CV_CMP_LE);    cvMul(I, mask, I);    cvMul(H, S, H);    cvMul(H, I, hsi_chk);    cvConvertScale(hsi_chk, hsi_chk, 1.0 / 255);        cvReleaseImage(&H);    cvReleaseImage(&S);    cvReleaseImage(&I);    cvReleaseImage(&mask);    return 0;}
    根据实验经验,博主给出一组参考阈值:    H_min = 0    H_max = 60              S_min = 20   S_max = 100    I_min = 100  I_max = 255    参考上面给出的色相环可以轻松设定H分量的两个阈值,如果是想要检测其他颜色的火焰,可以根据需要做修改。    S分量的阈值较小,这与火焰所处的环境有关,若是在露天较明亮的地方检验,则需要调低一点;若在室内较暗的地方检验,则需要调高一点。    I分量的阈值较大,依据这么一个先验知识:火焰是会发光的。    最后,我们代入参考阈值检验一下图片看看效果如何:

这里写图片描述

这里写图片描述

    我们可以看出,火焰的基本轮廓都提取出来了。若大家想要把整个区域都提取出来,可以自行尝试其它阈值。    今天就分享到这里~欢迎大家多来评论,来找出博主可能存在的错误,希望能和各位图像爱好者共同成长~    那么我们下次见~下文预告:基于OpenCV的火焰检测(四)——RGB与HSI的结合判据
0 0
原创粉丝点击