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

来源:互联网 发布:中天证券交易软件 编辑:程序博客网 时间:2024/04/29 23:33

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

基本的算法描述如下:


代码:

#include "opencv_libs.h"#include <highgui.h>#include <cv.h>#include <math.h>/* * 描述:基于HSI颜色模型消除红眼 * 作者:qdsclove(qdsclove@gmail.com) * 时间:22:49 4/18 星期四 2013 *//* 鼠标选择的矩形 */CvRect rect;/* 标记是否在画 */bool draw = false;/* 确定下来的眼睛矩形 */CvRect eyeRect;/* 是否选定眼睛区域 */bool isFinalRect = false;IplImage* img;  IplImage* temp;  IplImage* original; // 将HSI颜色空间的三个分量组合起来,便于显示IplImage* catHSImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I){IplImage* HSI_Image = cvCreateImage( cvGetSize( HSI_H ), IPL_DEPTH_8U, 3 );for(int i = 0; i < HSI_Image->height; i++){for(int j = 0; j < HSI_Image->width; j++){double d = cvmGet( HSI_H, i, j );int b = (int)(d * 255/360);d = cvmGet( HSI_S, i, j );int g = (int)( d * 255 );d = cvmGet( HSI_I, i, j );int r = (int)( d * 255 );cvSet2D( HSI_Image, i, j, cvScalar( b, g, r ) );}}return HSI_Image;}// 将HSI颜色模型的数据转换为RGB颜色模型的图像IplImage* HSI2RGBImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I){IplImage * RGB_Image = cvCreateImage(cvGetSize(HSI_H), IPL_DEPTH_8U, 3 );int iB, iG, iR;for(int i = 0; i < RGB_Image->height; i++){for(int j = 0; j < RGB_Image->width; j++){// 该点的色度Hdouble dH = cvmGet( HSI_H, i, j );// 该点的色饱和度Sdouble dS = cvmGet( HSI_S, i, j );// 该点的亮度double dI = cvmGet( HSI_I, i, j );double dTempB, dTempG, dTempR;// RG扇区if(dH < 120 && dH >= 0){// 将H转为弧度表示dH = dH * 3.1415926 / 180;dTempB = dI * (1 - dS);dTempR = dI * ( 1 + (dS * cos(dH))/cos(3.1415926/3 - dH) );dTempG = (3 * dI - (dTempR + dTempB)); }// GB扇区else if(dH < 240 && dH >= 120){dH -= 120;// 将H转为弧度表示dH = dH * 3.1415926 / 180;dTempR = dI * (1 - dS);dTempG = dI * (1 + dS * cos(dH)/cos(3.1415926/3 - dH));dTempB = (3 * dI - (dTempR + dTempG));}// BR扇区else {dH -= 240;// 将H转为弧度表示dH = dH * 3.1415926 / 180;dTempG = dI * (1 - dS);dTempB = dI * (1 + (dS * cos(dH))/cos(3.1415926/3 - dH));dTempR = (3* dI - (dTempG + dTempB));}iB = dTempB * 255;iG = dTempG * 255;iR = dTempR * 255;cvSet2D( RGB_Image, i, j, cvScalar( iB, iG, iR ) );}}return RGB_Image;}// 利用HSI颜色空间去除红眼void EraseRedEye(IplImage* img, CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I){// 原始图像数据指针, HSI矩阵数据指针uchar* data;// rgb分量byte img_r, img_g, img_b;byte min_rgb;  // rgb分量中的最小值// HSI分量float fHue, fSaturation, fIntensity; for(int i = 0; i < HSI_H->height; i++){for(int j = 0; j < HSI_H->width; j++){ data = cvPtr2D(img, i, j, 0);   img_b = *data; data++; img_g = *data; data++; img_r = *data; // Intensity分量[0, 1] fIntensity = (float)((img_b + img_g + img_r)/3)/255; // 得到RGB分量中的最小值 float fTemp = img_r < img_g ? img_r : img_g; min_rgb = fTemp < img_b ? fTemp : img_b; // Saturation分量[0, 1] fSaturation = 1 - (float)(3 * min_rgb)/(img_r + img_g + img_b); // 计算theta角 float numerator = (img_r - img_g + img_r - img_b ) / 2; float denominator = sqrt(  pow( (img_r - img_g), 2 ) + (img_r - img_b)*(img_g - img_b) ); // 计算Hue分量 if(denominator != 0) { float theta = acos( numerator/denominator) * 180/3.14;  if(img_b <= img_g) { fHue = theta ; } else { fHue = 360 - theta; } } else { fHue = 0; } // 赋值 cvmSet( HSI_H, i, j, fHue );  // 若为红眼像素,进行处理 if( (fHue <= 45 || fHue >= 315 ) && fSaturation > 0.3) { // 设置该像素点饱和度为零 cvmSet( HSI_S, i, j, 0); } else { cvmSet( HSI_S, i, j, fSaturation); } cvmSet( HSI_I, i, j, fIntensity );}}}void draw_rect(IplImage* img, CvRect rect)  {      cvRectangle( img,           cvPoint( rect.x, rect.y ),          cvPoint( rect.x + rect.width, rect.y + rect.height),          cvScalar( 0xff, 0x00, 0x00) );  }    // 鼠标回调函数  void my_mouse_callback( int event, int x, int y, int flags, void* param)  {      IplImage* image = (IplImage*) param;        switch( event )      {      case CV_EVENT_MOUSEMOVE:          {              if(draw)              {                  rect.width = x - rect.x;                  rect.height = y - rect.y;              }          }          break;      case CV_EVENT_LBUTTONDOWN:          {              draw = true;              rect = cvRect( x, y, 0, 0 );          }          break;      case CV_EVENT_LBUTTONUP:          {              draw = false;              if(rect.width < 0)              {                  rect.x += rect.width;                  rect.width *= -1;              }              if(rect.height < 0)              {                  rect.y += rect.height;                  rect.height *= -1;              }              // draw              draw_rect(image, rect);  isFinalRect = true;eyeRect = cvRect( rect.x, rect.y, rect.width, rect.height );printf("(%d, %d), %d\t%d", rect.x, rect.y, rect.width, rect.height );        }          break;          // 在右键按下时清除      case CV_EVENT_RBUTTONDOWN:          cvCopyImage(original, img);          printf("clear.\n");          break;      }  }  int main(){IplImage* img = cvLoadImage("redeye3.jpg");rect = cvRect( -1, -1, 0, 0);  eyeRect = cvRect(-1, -1, 0, 0);// 副本  temp = cvCreateImage( cvGetSize( img ), img->depth, img->nChannels );original = cvCreateImage( cvGetSize( img ), img->depth, img->nChannels );cvCopyImage( img, temp );  cvCopyImage( img, original );  cvNamedWindow("去除红眼");      cvSetMouseCallback("去除红眼", my_mouse_callback, (void*)img);        while(1)      {          cvCopyImage(img, temp);            if(draw)          {              draw_rect( temp , rect );          }            cvShowImage( "去除红眼", temp);            if(cvWaitKey(15) == 27)              break;  if(isFinalRect == true){goto erase;}    }  // 去除红眼erase: cvCopyImage(original, img);  // 三个HSI空间数据矩阵CvMat* HSI_H = cvCreateMat( eyeRect.height, eyeRect.width, CV_32FC1 );CvMat* HSI_S = cvCreateMat( eyeRect.height, eyeRect.width, CV_32FC1 );CvMat* HSI_I = cvCreateMat( eyeRect.height, eyeRect.width, CV_32FC1 );// 设置图像感兴趣区域cvSetImageROI( img, eyeRect );EraseRedEye(img, HSI_H, HSI_S, HSI_I );IplImage* HSI_Image = catHSImage( HSI_H, HSI_S, HSI_I );IplImage* RGB_Image = HSI2RGBImage( HSI_H, HSI_S, HSI_I );cvShowImage("原始图像", img);cvCopyImage( RGB_Image, img );cvResetImageROI( img );cvShowImage("红眼擦除后", img );cvWaitKey(0);cvWaitKey(0);cvReleaseImage( &img );cvReleaseImage( &original );cvReleaseImage( &temp );cvReleaseImage( &HSI_Image );cvReleaseImage( &RGB_Image );cvReleaseMat( &HSI_H );cvReleaseMat( &HSI_S );cvReleaseMat( &HSI_I );cvDestroyAllWindows();return 0;}

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

效果图:





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