自动红眼移除算法 附c++完整代码

内容预览:
  • 当然其实最简单的思路,就是转色域空间处理后再转回RGB~
  • 直到2016年,在一个Delphi 图像控件的源码里看到了一个红颜移除算...~
  • 貌似好像有点暴露年龄了, 俺也曾经是Delphi程序员来的,无比怀念Delphi7~

说起红眼算法,这个话题非常古老了。

百度百科上的描述:

“红眼”一般是指在人物摄影时,当闪光灯照射到人眼的时候,瞳孔放大而产生的视网膜泛红现象。

由于红眼现象的程度是根据拍摄对象色素的深浅决定的,如果拍摄对象的眼睛颜色较深,红眼现象便不会特别明显。

“红眼”也指传染性结膜炎。

 

近些年好像没有看到摄影会出现这样的情况,毕竟科技发展迅速。

记得最早看到红眼移除算法是在ACDSee 这个看图软件的编辑功能区。

当然,当时ACDSee 也没有能力做到自动去红眼,也需要进行手工操作。

红眼移除不难,其实就是把眼睛区域的颜色修正一下。

但是难就难在修复之后,不要显得太过突兀,或者破坏眼睛周围的颜色 。

这就有点难办了。

当然其实最简单的思路,就是转色域空间处理后再转回RGB。

记得在2015年的时候,

曾经一度想要寻找红眼移除过度自然的算法思路,

当时仅仅是好奇,想要学习之。

直到2016年,在一个Delphi 图像控件的源码里看到了一个红颜移除算法函数。

把代码转写成C之后验证了一下,效果不错,过度很自然。

貌似好像有点暴露年龄了,

俺也曾经是Delphi程序员来的,无比怀念Delphi7。

贴上红眼算法的Delphi源码: 

procedure _IERemoveRedEyes(bitmap: TIEBitmap; fSelx1, fSely1, fSelx2, fSely2: integer; fOnProgress: TIEProgressEvent; Sender: TObject);
var
row, col: integer;
nrv, bluf, redq, powr, powb, powg: double;
per1: double;
px: PRGB;
begin
fSelX2 :
= imin(fSelX2, bitmap.Width); dec(fSelX2);
fSelY2 :
= imin(fSelY2, bitmap.Height); dec(fSelY2);
per1 :
= 100 / (fSelY2 - fSelY1 + 0.5);
for row := fSelY1 to fSelY2 do
begin
px :
= bitmap.Scanline[row];
for col := fSelX1 to fSelX2 do
begin
nrv :
= px^.g + px^.b;
if nrv < 1 then
nrv :
= 1;
if px^.g > 1 then
bluf :
= px^.b / px^.g
else
bluf :
= px^.b;
bluf :
= dMax(0.5, dMin(1.5, Sqrt(bluf)));
redq :
= (px^.r / nrv) * bluf;
if redq > 0.7 then
begin
powr :
= 1.775 - (redq * 0.75 + 0.25);
if powr < 0 then
powr :
= 0;
powr :
= powr * powr;
powb :
= 1 - (1 - powr) / 2;
powg :
= 1 - (1 - powr) / 4;
with px^ do
begin
r :
= Round(powr * r);
b :
= Round(powb * b);
g :
= Round(powg * g);
end;
end;
inc(px);
end;
if assigned(fOnProgress) then
fOnProgress(Sender, trunc(per1
* (row - fSelY1 + 1)));
Application.ProcessMessages;
end;
end;

非常非常简单的代码。

但是思路很巧妙。

不多说,各位看官自己品味一下。

先上个效果图:

 

说明下本文背景前提:

人脸识别暂时采用MTCNN,示例不考虑判断是否存在红眼。

人脸检测部分,详情见博文《MTCNN人脸检测 附完整C++代码

算法步骤:

检测人脸,对齐得到人脸五个特征点。

算出两眼球之间的距离,

估算眼球的大概大小,

(示例代码采用 两眼球之间的距离的九分之一)

计算相应的半径,

按圆形修复眼球颜色即可。

 

完整示例代码献上:

#include "mtcnn.h"
#include
"browse.h"
#define USE_SHELL_OPEN
#ifndef nullptr
#define nullptr 0
#endif
#if defined(_MSC_VER)
#define _CRT_SECURE_NO_WARNINGS
#include
<windows.h>
#else
#include
<unistd.h>
#endif
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION

#include
"stb_image.h"
//ref:https://github.com/nothings/stb/blob/master/stb_image.h
#define TJE_IMPLEMENTATION

#include
"tiny_jpeg.h"
//ref:https://github.com/serge-rgb/TinyJPEG/blob/master/tiny_jpeg.h

#include
<stdint.h>
#include
"timing.h"

char saveFile[1024];

unsigned
char *loadImage(const char *filename, int *Width, int *Height, int *Channels) {
return stbi_load(filename, Width, Height, Channels, 0);
}

void saveImage(const char *filename, int Width, int Height, int Channels, unsigned char *Output) {
memcpy(saveFile
+ strlen(saveFile), filename, strlen(filename));
*(saveFile + strlen(saveFile) + 1) = 0;
//保存为jpg
if (!tje_encode_to_file(saveFile, Width, Height, Channels, true, Output)) {
fprintf(stderr,
"save JPEG fail.n");
return;
}

#ifdef USE_SHELL_OPEN
browse(saveFile);
#endif
}

void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) {
const char *end;
const char *p;
const char *s;
if (path[0] && path[1] == ':') {
if (drv) {
*drv++ = *path++;
*drv++ = *path++;
*drv = '
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论