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

参考自:http://www.cnblogs.com/qdsclove/archive/2013/04/18/3029738.html

基本的算法描述如下:

代码:

复制代码
  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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值