您的位置:首页 > 其它

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

2015-09-19 20:47 405 查看


[cpp] view
plaincopy

#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++)

{

// 该点的色度H

double dH = cvmGet( HSI_H, i, j );

// 该点的色饱和度S

double 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;

}

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

效果图:







未来的实现:利用人脸检测自动定位到眼睛区域,不用鼠标选择眼睛区域。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: