zoukankan      html  css  js  c++  java
  • 基于HSI颜色模型实现去除照片的“红眼现象”

    接上篇博文中的RGB颜色模型转换为HSI颜色模型,做了消除红眼的算法。

    基本的算法描述如下:

    代码:

      1 #include "opencv_libs.h"
      2 #include <highgui.h>
      3 #include <cv.h>
      4 #include <math.h>
      5 
      6 /*
      7  * 描述:基于HSI颜色模型消除红眼
      8  * 作者:qdsclove(qdsclove@gmail.com)
      9  * 时间:22:49 4/18 星期四 2013
     10  */
     11 
     12 /* 鼠标选择的矩形 */
     13 CvRect rect;
     14 /* 标记是否在画 */
     15 bool draw = false;
     16 /* 确定下来的眼睛矩形 */
     17 CvRect eyeRect;
     18 /* 是否选定眼睛区域 */
     19 bool isFinalRect = false;
     20 
     21 IplImage* img;  
     22 IplImage* temp;  
     23 IplImage* original; 
     24 
     25 // 将HSI颜色空间的三个分量组合起来,便于显示
     26 IplImage* catHSImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
     27 {
     28     IplImage* HSI_Image = cvCreateImage( cvGetSize( HSI_H ), IPL_DEPTH_8U, 3 );
     29 
     30     for(int i = 0; i < HSI_Image->height; i++)
     31     {
     32         for(int j = 0; j < HSI_Image->width; j++)
     33         {
     34             double d = cvmGet( HSI_H, i, j );
     35             int b = (int)(d * 255/360);
     36             d = cvmGet( HSI_S, i, j );
     37             int g = (int)( d * 255 );
     38             d = cvmGet( HSI_I, i, j );
     39             int r = (int)( d * 255 );
     40 
     41             cvSet2D( HSI_Image, i, j, cvScalar( b, g, r ) );
     42         }
     43     }
     44     return HSI_Image;
     45 }
     46 
     47 // 将HSI颜色模型的数据转换为RGB颜色模型的图像
     48 IplImage* HSI2RGBImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
     49 {
     50     IplImage * RGB_Image = cvCreateImage(cvGetSize(HSI_H), IPL_DEPTH_8U, 3 );
     51 
     52     int iB, iG, iR;
     53     for(int i = 0; i < RGB_Image->height; i++)
     54     {
     55         for(int j = 0; j < RGB_Image->width; j++)
     56         {
     57             // 该点的色度H
     58             double dH = cvmGet( HSI_H, i, j );
     59             // 该点的色饱和度S
     60             double dS = cvmGet( HSI_S, i, j );
     61             // 该点的亮度
     62             double dI = cvmGet( HSI_I, i, j );
     63 
     64             double dTempB, dTempG, dTempR;
     65             // RG扇区
     66             if(dH < 120 && dH >= 0)
     67             {
     68                 // 将H转为弧度表示
     69                 dH = dH * 3.1415926 / 180;
     70                 dTempB = dI * (1 - dS);
     71                 dTempR = dI * ( 1 + (dS * cos(dH))/cos(3.1415926/3 - dH) );
     72                 dTempG = (3 * dI - (dTempR + dTempB)); 
     73             }
     74             // GB扇区
     75             else if(dH < 240 && dH >= 120)
     76             {
     77                 dH -= 120;
     78                                 
     79                 // 将H转为弧度表示
     80                 dH = dH * 3.1415926 / 180;
     81 
     82                 dTempR = dI * (1 - dS);
     83                 dTempG = dI * (1 + dS * cos(dH)/cos(3.1415926/3 - dH));
     84                 dTempB = (3 * dI - (dTempR + dTempG));
     85             }
     86             // BR扇区
     87             else 
     88             {
     89                 dH -= 240;
     90 
     91                 // 将H转为弧度表示
     92                 dH = dH * 3.1415926 / 180;
     93 
     94                 dTempG = dI * (1 - dS);
     95                 dTempB = dI * (1 + (dS * cos(dH))/cos(3.1415926/3 - dH));
     96                 dTempR = (3* dI - (dTempG + dTempB));
     97             }
     98 
     99             iB = dTempB * 255;
    100             iG = dTempG * 255;
    101             iR = dTempR * 255;
    102 
    103             cvSet2D( RGB_Image, i, j, cvScalar( iB, iG, iR ) );
    104         }
    105     }
    106     return RGB_Image;
    107 }
    108 
    109 // 利用HSI颜色空间去除红眼
    110 void EraseRedEye(IplImage* img, CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
    111 {
    112     // 原始图像数据指针, HSI矩阵数据指针
    113     uchar* data;
    114 
    115     // rgb分量
    116     byte img_r, img_g, img_b;
    117     byte min_rgb;  // rgb分量中的最小值
    118     // HSI分量
    119     float fHue, fSaturation, fIntensity; 
    120 
    121     for(int i = 0; i < HSI_H->height; i++)
    122     {
    123         for(int j = 0; j < HSI_H->width; j++)
    124         {
    125              data = cvPtr2D(img, i, j, 0);  
    126              img_b = *data;
    127              data++;
    128              img_g = *data;
    129              data++;
    130              img_r = *data;
    131 
    132              // Intensity分量[0, 1]
    133              fIntensity = (float)((img_b + img_g + img_r)/3)/255;
    134 
    135              // 得到RGB分量中的最小值
    136              float fTemp = img_r < img_g ? img_r : img_g;
    137              min_rgb = fTemp < img_b ? fTemp : img_b;
    138              // Saturation分量[0, 1]
    139              fSaturation = 1 - (float)(3 * min_rgb)/(img_r + img_g + img_b);
    140 
    141              // 计算theta角
    142              float numerator = (img_r - img_g + img_r - img_b ) / 2;
    143              float denominator = sqrt( 
    144                  pow( (img_r - img_g), 2 ) + (img_r - img_b)*(img_g - img_b) );
    145 
    146              // 计算Hue分量
    147              if(denominator != 0)
    148              {
    149                  float theta = acos( numerator/denominator) * 180/3.14;
    150                  
    151                  if(img_b <= img_g)
    152                  {
    153                      fHue = theta ;
    154                  }
    155                  else
    156                  {
    157                      fHue = 360 - theta;
    158                  }
    159              }
    160              else
    161              {
    162                  fHue = 0;
    163              }
    164 
    165              // 赋值
    166              cvmSet( HSI_H, i, j, fHue );
    167              
    168              // 若为红眼像素,进行处理
    169              if( (fHue <= 45 || fHue >= 315 ) && fSaturation > 0.3)
    170              {
    171                  // 设置该像素点饱和度为零
    172                  cvmSet( HSI_S, i, j, 0);
    173              }
    174              else
    175              {
    176                  cvmSet( HSI_S, i, j, fSaturation);
    177              }
    178              cvmSet( HSI_I, i, j, fIntensity );
    179         }
    180     }
    181 }
    182 
    183 void draw_rect(IplImage* img, CvRect rect)  
    184 {  
    185     cvRectangle( img,   
    186         cvPoint( rect.x, rect.y ),  
    187         cvPoint( rect.x + rect.width, rect.y + rect.height),  
    188         cvScalar( 0xff, 0x00, 0x00) );  
    189 }  
    190   
    191 // 鼠标回调函数  
    192 void my_mouse_callback( int event, int x, int y, int flags, void* param)  
    193 {  
    194     IplImage* image = (IplImage*) param;  
    195   
    196     switch( event )  
    197     {  
    198     case CV_EVENT_MOUSEMOVE:  
    199         {  
    200             if(draw)  
    201             {  
    202                 rect.width = x - rect.x;  
    203                 rect.height = y - rect.y;  
    204             }  
    205         }  
    206         break;  
    207     case CV_EVENT_LBUTTONDOWN:  
    208         {  
    209             draw = true;  
    210             rect = cvRect( x, y, 0, 0 );  
    211         }  
    212         break;  
    213     case CV_EVENT_LBUTTONUP:  
    214         {  
    215             draw = false;  
    216             if(rect.width < 0)  
    217             {  
    218                 rect.x += rect.width;  
    219                 rect.width *= -1;  
    220             }  
    221             if(rect.height < 0)  
    222             {  
    223                 rect.y += rect.height;  
    224                 rect.height *= -1;  
    225             }  
    226             // draw  
    227             draw_rect(image, rect);  
    228 
    229             isFinalRect = true;
    230             eyeRect = cvRect( rect.x, rect.y, rect.width, rect.height );
    231             printf("(%d, %d), %d\t%d", rect.x, rect.y, rect.width, rect.height );
    232         }  
    233         break;  
    234         // 在右键按下时清除  
    235     case CV_EVENT_RBUTTONDOWN:  
    236         cvCopyImage(original, img);  
    237         printf("clear.\n");  
    238         break;  
    239     }  
    240 }  
    241 
    242 int main()
    243 {
    244     IplImage* img = cvLoadImage("redeye3.jpg");
    245     
    246     rect = cvRect( -1, -1, 0, 0);  
    247     eyeRect = cvRect(-1, -1, 0, 0);
    248 
    249     // 副本  
    250     temp = cvCreateImage( cvGetSize( img ), img->depth, img->nChannels );
    251     original = cvCreateImage( cvGetSize( img ), img->depth, img->nChannels );
    252     cvCopyImage( img, temp );  
    253     cvCopyImage( img, original );  
    254 
    255     cvNamedWindow("去除红眼");  
    256     cvSetMouseCallback("去除红眼", my_mouse_callback, (void*)img);  
    257   
    258     while(1)  
    259     {  
    260         cvCopyImage(img, temp);  
    261   
    262         if(draw)  
    263         {  
    264             draw_rect( temp , rect );  
    265         }  
    266   
    267         cvShowImage( "去除红眼", temp);  
    268   
    269         if(cvWaitKey(15) == 27)  
    270             break;  
    271 
    272         if(isFinalRect == true)
    273         {
    274             goto erase;
    275         }
    276     }  
    277     // 去除红眼
    278 erase: 
    279     cvCopyImage(original, img);  
    280     
    281 
    282     // 三个HSI空间数据矩阵
    283     CvMat* HSI_H = cvCreateMat( eyeRect.height, eyeRect.width, CV_32FC1 );
    284     CvMat* HSI_S = cvCreateMat( eyeRect.height, eyeRect.width, CV_32FC1 );
    285     CvMat* HSI_I = cvCreateMat( eyeRect.height, eyeRect.width, CV_32FC1 );
    286 
    287     // 设置图像感兴趣区域
    288     cvSetImageROI( img, eyeRect );
    289 
    290     EraseRedEye(img, HSI_H, HSI_S, HSI_I );
    291 
    292     IplImage* HSI_Image = catHSImage( HSI_H, HSI_S, HSI_I );
    293     IplImage* RGB_Image = HSI2RGBImage( HSI_H, HSI_S, HSI_I );
    294 
    295     cvShowImage("原始图像", img);
    296     cvCopyImage( RGB_Image, img );
    297 
    298     cvResetImageROI( img );
    299     cvShowImage("红眼擦除后", img );
    300 
    301     cvWaitKey(0);
    302     cvWaitKey(0);
    303 
    304     cvReleaseImage( &img );
    305     cvReleaseImage( &original );
    306     cvReleaseImage( &temp );
    307     cvReleaseImage( &HSI_Image );
    308     cvReleaseImage( &RGB_Image );
    309     cvReleaseMat( &HSI_H );
    310     cvReleaseMat( &HSI_S );
    311     cvReleaseMat( &HSI_I );
    312 
    313     cvDestroyAllWindows();
    314 
    315     return 0;
    316 }

    首先鼠标选择照片的眼睛区域,然后程序实现处理。

    效果图:

    未来的实现:利用人脸检测自动定位到眼睛区域,不用鼠标选择眼睛区域。

  • 相关阅读:
    用python执行Linux命令
    ls用法
    frigate_TUNNEL
    Python读写Excel文件的实例
    python操作Excel读写--使用xlrd
    iptables详解
    IPy过滤
    python 类中__call__内置函数的使用
    python 类中__init__函数的使用
    超继承
  • 原文地址:https://www.cnblogs.com/qdsclove/p/3029738.html
Copyright © 2011-2022 走看看