基于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;
}
首先鼠标选择照片的眼睛区域,然后程序实现处理。
效果图:
未来的实现:利用人脸检测自动定位到眼睛区域,不用鼠标选择眼睛区域。
相关文章推荐
- HDU 5200 Tree (离线并查集)
- HTML(网页设计)基础到加强
- 我所欣赏的一些学习习惯
- 数据结构之链表
- hdu 5452 Minimum Cut 求最小割边集的大小
- 使用C# 与Xero OAuth 交互 集成
- GBK 字节数分割字符数
- 用SQL Server实现建表和实现增删改
- 阐述linux IPC(两):基于socket进程间通信(下一个)
- SVN与eclipse整合和利用、SVN与Apache综合
- C# 设置MDI子窗体只能弹出一个的方法
- Python 脚本学习笔记(四) 定制业务质量报表
- Python 脚本学习笔记(四) 定制业务质量报表
- Python 脚本学习笔记(四) 定制业务质量报表
- 定制Enhancedgrid的description内容
- Effective C++——条款46(第7章)
- JAVA科学计数法转普通计数法
- C# 启动外部程序的几种常用方法汇总
- Java基础知识强化45:StringBuffer类之字符串反转的案例
- RPM