zoukankan      html  css  js  c++  java
  • 基于OpenCV的火焰检测(三)——HSI颜色判据

    上文向大家介绍了如何用最简单的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:
    ");
            printf("img_bgr == NULL || img_hsi == NULL
    ");
            return -1;
        }
    
        if (img_bgr->nChannels != 3 || img_hsi->nChannels != 3){
            printf("func cvBGR2HSI Error:
    ");
            printf("img_bgr->nChannels != 3 || img_hsi->nChannels != 3
    ");
            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:
    ");
            printf("img_hsi == NULL || hsi_chk == NULL)
    ");
            return -1;
        }
    
        if (img_hsi->nChannels != 3 || hsi_chk->nChannels != 1){
            printf("func cvHSI_CHK Error:
    ");
            printf("img_hsi->nChannels != 3 || hsi_chk->nChannels != 1)
    ");
            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分量的阈值较大,依据这么一个先验知识:火焰是会发光的。
    
        最后,我们代入参考阈值检验一下图片看看效果如何:
    

    这里写图片描述

    这里写图片描述

        我们可以看出,火焰的基本轮廓都提取出来了。若大家想要把整个区域都提取出来,可以自行尝试其它阈值。
    

  • 相关阅读:
    【Java】Java中的集合框架、泛型
    【Java】Java中日期获取、数值精度、异常处理
    【Java】包装类、及字符序列CharSequence接口
    【微服务】Docker环境安装及卸载教程-持续更新中
    【Java】Stream流式编程实战
    【Java】Lambda表达式详解及实战
    【Java】Java中I/O流使用及详解
    加入博客园的第八个月
    Structs复习 Structs标签
    Structs复习 OGNL
  • 原文地址:https://www.cnblogs.com/alan666/p/8312278.html
Copyright © 2011-2022 走看看