您的位置:首页 > 运维架构

OpenCV实现RGB颜色空间和HSI颜色空间的相互转换

2017-01-25 20:07 1096 查看
核心的转换公式:

RGB-->HSI



截图来自中科院刘定生老师的《数字图像处理与分析》课件。

HSI-->RGB

具体的数学公式参照冈萨雷斯版《数字图像处理(第三版)》432-434页,中译版的260-261页。

下面贴代码:

1 #include "opencv_libs.h"
2 #include <highgui.h>
3 #include <cv.h>
4 #include <math.h>
5
6 /*
7  * 描述:实现RGB颜色模型到HSI颜色模型之间的相互转换
8  * 作者:qdsclove(qdsclove@gmail.com)
9  * 时间:16:01 4/17 星期三 2013
10  */
11
12 // 将HSI颜色空间的三个分量组合起来,便于显示
13 IplImage* catHSImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
14 {
15     IplImage* HSI_Image = cvCreateImage( cvGetSize( HSI_H ), IPL_DEPTH_8U, 3 );
16
17     for(int i = 0; i < HSI_Image->height; i++)
18     {
19         for(int j = 0; j < HSI_Image->width; j++)
20         {
21             double d = cvmGet( HSI_H, i, j );
22             int b = (int)(d * 255/360);
23             d = cvmGet( HSI_S, i, j );
24             int g = (int)( d * 255 );
25             d = cvmGet( HSI_I, i, j );
26             int r = (int)( d * 255 );
27
28             cvSet2D( HSI_Image, i, j, cvScalar( b, g, r ) );
29         }
30     }
31
32     return HSI_Image;
33 }
34
35 // 将HSI颜色模型的数据转换为RGB颜色模型的图像
36 IplImage* HSI2RGBImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
37 {
38     IplImage * RGB_Image = cvCreateImage(cvGetSize(HSI_H), IPL_DEPTH_8U, 3 );
39
40     int iB, iG, iR;
41     for(int i = 0; i < RGB_Image->height; i++)
42     {
43         for(int j = 0; j < RGB_Image->width; j++)
44         {
45             // 该点的色度H
46             double dH = cvmGet( HSI_H, i, j );
47             // 该点的色饱和度S
48             double dS = cvmGet( HSI_S, i, j );
49             // 该点的亮度
50             double dI = cvmGet( HSI_I, i, j );
51
52             double dTempB, dTempG, dTempR;
53             // RG扇区
54             if(dH < 120 && dH >= 0)
55             {
56                 // 将H转为弧度表示
57                 dH = dH * 3.1415926 / 180;
58                 dTempB = dI * (1 - dS);
59                 dTempR = dI * ( 1 + (dS * cos(dH))/cos(3.1415926/3 - dH) );
60                 dTempG = (3 * dI - (dTempR + dTempB));
61             }
62             // GB扇区
63             else if(dH < 240 && dH >= 120)
64             {
65                 dH -= 120;
66
67                 // 将H转为弧度表示
68                 dH = dH * 3.1415926 / 180;
69
70                 dTempR = dI * (1 - dS);
71                 dTempG = dI * (1 + dS * cos(dH)/cos(3.1415926/3 - dH));
72                 dTempB = (3 * dI - (dTempR + dTempG));
73             }
74             // BR扇区
75             else
76             {
77                 dH -= 240;
78
79                 // 将H转为弧度表示
80                 dH = dH * 3.1415926 / 180;
81
82                 dTempG = dI * (1 - dS);
83                 dTempB = dI * (1 + (dS * cos(dH))/cos(3.1415926/3 - dH));
84                 dTempR = (3* dI - (dTempG + dTempB));
85             }
86
87             iB = dTempB * 255;
88             iG = dTempG * 255;
89             iR = dTempR * 255;
90
91             cvSet2D( RGB_Image, i, j, cvScalar( iB, iG, iR ) );
92         }
93     }
94
95     return RGB_Image;
96 }
97
98
99 int main()
100 {
101     IplImage* img = cvLoadImage("lena.bmp");
102
103     // 三个HSI空间数据矩阵
104     CvMat* HSI_H = cvCreateMat( img->height, img->width, CV_32FC1 );
105     CvMat* HSI_S = cvCreateMat( img->height, img->width, CV_32FC1 );
106     CvMat* HSI_I = cvCreateMat( img->height, img->width, CV_32FC1 );
107
108     // 原始图像数据指针, HSI矩阵数据指针
109     uchar* data;
110
111     // rgb分量
112     byte img_r, img_g, img_b;
113     byte min_rgb;  // rgb分量中的最小值
114     // HSI分量
115     float fHue, fSaturation, fIntensity;
116
117     for(int i = 0; i < img->height; i++)
118     {
119         for(int j = 0; j < img->width; j++)
120         {
121              data = cvPtr2D(img, i, j, 0);
122              img_b = *data;
123              data++;
124              img_g = *data;
125              data++;
126              img_r = *data;
127
128              // Intensity分量[0, 1]
129              fIntensity = (float)((img_b + img_g + img_r)/3)/255;
130
131              // 得到RGB分量中的最小值
132              float fTemp = img_r < img_g ? img_r : img_g;
133              min_rgb = fTemp < img_b ? fTemp : img_b;
134              // Saturation分量[0, 1]
135              fSaturation = 1 - (float)(3 * min_rgb)/(img_r + img_g + img_b);
136
137              // 计算theta角
138              float numerator = (img_r - img_g + img_r - img_b ) / 2;
139              float denominator = sqrt(
140                  pow( (img_r - img_g), 2 ) + (img_r - img_b)*(img_g - img_b) );
141
142              // 计算Hue分量
143              if(denominator != 0)
144              {
145                  float theta = acos( numerator/denominator) * 180/3.14;
146
147                  if(img_b <= img_g)
148                  {
149                      fHue = theta ;
150                  }
151                  else
152                  {
153                      fHue = 360 - theta;
154                  }
155              }
156              else
157              {
158                  fHue = 0;
159              }
160
161              // 赋值
162              cvmSet( HSI_H, i, j, fHue );
163              cvmSet( HSI_S, i, j, fSaturation);
164              cvmSet( HSI_I, i, j, fIntensity );
165         }
166     }
167
168     IplImage* HSI_Image = catHSImage( HSI_H, HSI_S, HSI_I );
169     IplImage* RGB_Image = HSI2RGBImage( HSI_H, HSI_S, HSI_I );
170
171     cvShowImage("img", img);
172     cvShowImage("HSI Color Model", HSI_Image);
173     cvShowImage("RGB Color Model", RGB_Image);
174
175     cvWaitKey(0);
176
177     cvReleaseImage( &img );
178     cvReleaseImage( &HSI_Image );
179     cvReleaseImage( &RGB_Image );
180     cvReleaseMat( &HSI_H);
181     cvReleaseMat( &HSI_S);
182     cvReleaseMat( &HSI_I);
183
184     cvDestroyAllWindows();
185
186     return 0;
187 }

另一种方法:
 对彩色图像处理的时候,一般都是基于颜色空间来做的,比如要检测一片连续的蓝色,就可以基于颜色空间来实现分割。大家首先想到的是RGB,但是这里有个问题。RGB关联太大,每个通道都编入了亮度信息,容易受周围环境的处理,比如说光照不足。而HSI空间就不会。HSI空间是从人的视觉系统出发用色调(Hue), 色饱和度(Situration)和亮度(Intensity)来描述颜色,一旦用HSI空间就可以从色调通道中分离出蓝色色调,蓝色色调差不多在220左右。

    下面要说的RGB到HSI空间的转换。我是用OPENCV来实现图像处理的,OPENCV读入图像是存储在IplImage结构中。对于彩色图像可以分开成三个通道存储 cvLoadImage(imageName, 1);三个通道依次是blue, green, red. 在OPENCV中并没有实现转化成HSI空间,这个我们得针对每个像素点自己手动写,公式在上面那个链接里。    上代码
    
// HSI.cpp : RGB  TO  HSI并显示出来

//

#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <cvaux.h>

int min(int a, int b, int c);

int main(int argc, char ** argv)
{
    IplImage * Img  = cvLoadImage(argv[1], 1);
    IplImage * HImg = cvCreateImage(cvGetSize(Img), IPL_DEPTH_8U, 1);
    IplImage * SImg = cvCreateImage(cvGetSize(Img), IPL_DEPTH_8U, 1);
    IplImage * IImg = cvCreateImage(cvGetSize(Img), IPL_DEPTH_8U, 1);    

    if( Rgb2Hsi(Img, HImg, SImg, IImg) == 0)
    {
        printf("Convert Error!\n");
        exit(-1);
    }
    
    //高斯滤波先,以平滑图像

    cvSmooth(SImg, SImg, CV_GAUSSIAN, 3, 0, 0, 0);
//    cvThreshold(SImg, SImg, 100, 255, CV_THRESH_BINARY);

    
    IplImage *pImgCanny = cvCreateImage(cvGetSize(SImg), IPL_DEPTH_8U, 1);
    cvCanny(SImg, pImgCanny, 50, 150, 3);
    
    cvNamedWindow("Img", 1);    
    cvNamedWindow("HImg", 1);
    cvNamedWindow("SImg", 1);
    cvNamedWindow("IImg", 1);

    cvShowImage("Img", Img);
    cvShowImage("HImg", HImg);
    cvShowImage("SImg", SImg);
    cvShowImage("IImg", IImg);

    cvWaitKey(0);
    return 0;
}

int Rgb2Hsi(const IplImage* src, IplImage* dataH, IplImage* dataS, IplImage* dataI)
{
    //返回0:成功,1:失败

    //输入为彩色图像src, 输出dataH,dataS,dataI,分别为HSI模型的H、S、I分量

    int  y,x,offsetSrc,offsetHsi;
    int step,channels,step1;
    int r,g,b,minRgb;
    double cosThita,num,den,S,H;
    uchar *dataImg;
    uchar *dataS1;
    uchar *dataI1;
    uchar *dataH1;

    channels = src->nChannels;
    dataImg = (uchar *)src->imageData;

    dataI1 = (uchar *)dataI->imageData;
    dataH1=(uchar *)dataH->imageData;
    dataS1=(uchar *)dataS->imageData;

    if (channels!=3)
    {
        printf("The input image is not 3-channels!\n");
        return 0;
    }

    step=src->widthStep;
    step1=dataI->widthStep;

    for (y=0; y<src->height; y++)
       for (x=0; x<src->width; x++)
        {
            offsetSrc = y*step + x*channels;
            offsetHsi=y*step1+x;

            b = dataImg[offsetSrc];
            g=dataImg[offsetSrc+1];
            r=dataImg[offsetSrc+2];

            dataI1[offsetHsi] = (int)((r+g+b)/3+0.5);
            num=(2*r-g-b)/2;
            den=sqrt((r-g)*(r-g)+(r-b)*(g-b));
            if (den==0)
                den=0.01;
            cosThita=acos(num/den);
            minRgb = min(r, g, b);

            den=r+g+b;
            if (den==0)
                den=0.01;
            S=1-3*minRgb/den;

            dataS1[offsetHsi]=(int) (S*255+0.5);//将S分量和H分量都扩充到[0,255]区间以便于显示

                                       //一般H分量在[0,2pi]之间,S在[0,1]之间

    
            if (b<=g)
                H=cosThita/(2*3.14);
            else
                H=(2*3.14-cosThita)/(2*3.14);
            dataH1[offsetHsi] = (int)(H*255+0.5);
            if (S==0)
                dataH1[offsetHsi]=0;

        }

    return 1;

}

int min(int a, int b, int c)
{
    int m = a;
    if(m > b)
        m = b;
    if( m > c)
        m = c;

    return m;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: