基于HSI颜色模型实现去除照片的“红眼现象”

来源:互联网 发布:淘宝网怎么注册用户 编辑:程序博客网 时间:2024/04/29 18:38


[cpp] view plaincopy
  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. }  

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

效果图:





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


0 0