OpenCV实现照片自动红眼去除
OpenCV实现照片自动红眼去除
使用闪光照相机拍照,在光线条件不足的情况,如果眼睛盯着相机镜头很容易造成拍出的照片中人眼球变成红色,虽然现在相机从系统和镜头上做了大量改进工作,防止这种情况发生,但是还是会出现这样的情况。这些照片后期可以通过PS手段进行修复,去除红眼得到正常照片显示。而做图像处理开发者可以借助OpenCV提供API功能轻松实现自动红眼去除修复。首先看一下效果吧图像
有红眼照片
修复之后的照片
实现步骤
眼睛检测
基于OpenCV自带的HAAR眼睛级联分类器特征数据(haarcascade_eye.xml),通过调用级联分类器实现眼睛检测,对检测到的眼睛用红色矩形框标注,如第一张图所示。
实现代码如下:
vector<Rect> eyes;cvtColor(src, gray, COLOR_BGR2GRAY);equalizeHist(gray, gray);eye_detector.detectMultiScale(gray, eyes, 1.1, 3, 0, Size(100, 100));
提取红色眼球区域
根据红眼颜色特征可以看出这种情况下,对每个像素点来说在RGB三通道中红色通道的分量明显要大于其它两个通道的值,所以通过三个通道分离,对每个像素红色通道来说值大于150(R>150)而且R >(G+B)时候我们把它保留作为眼球区域。最终对两个眼睛提取区域图像如下:
代码实现如下:
for (size_t t = 0; t < eyes.size(); t++) { // 通道分离 Mat eye = src(eyes[t]); vector<Mat>bgr(3); split(eye, bgr); // 基于像素模型的红眼区域检测 Mat mask = (bgr[2] > 150) & (bgr[2] > (bgr[1] + bgr[0]));}
逻辑操作与形态学处理
上述的白色区域就是红色眼球所在的区域,但是这样的情况下白色区域内部还有一些黑色小块,我们可以通过漫水填充与逻辑操作完整填充整个内部区域,然后通过形态的膨胀操作,让红色眼球进一步扩展,这样就会让整个处理之后的边界处看上去比较自然圆润一点。处理之后的效果如下:
代码实现如下:
// 区域填充与提取Mat mask_floodfill = mask.clone();floodFill(mask_floodfill, cv::Point(0, 0), Scalar(255));Mat mask2;bitwise_not(mask_floodfill, mask2);mask = (mask2 | mask);dilate(mask, mask, Mat(), Point(-1, -1), 3, 1, 1);
红眼修复
对上面得到白色区域即为红眼区域,通过把上面结果作为遮罩,对检测到人眼区域进行修复即可。一般情况下人的眼球都是黑色,越中心地方越黑色越暗,对白色区域内的每个像素点,取它的B和G两个通道的平均值作为修复处理之后的R,G,B三通道的值,这样就得到修复之后的眼球区域,然后用修复之后的眼球区域替代原来的红眼区域即可得到修复之后的图像:
代码实现如下:
// 修复Mat mean = (bgr[0] + bgr[1]) / 2;mean.copyTo(bgr[0], mask);mean.copyTo(bgr[1], mask);mean.copyTo(bgr[2], mask);// 回填Mat eyeOut;merge(bgr, eyeOut);eyeOut.copyTo(imgOut(eyes[t]));
完整代码如下
#include <opencv2/opencv.hpp>#include <iostream>using namespace cv;using namespace std;String filename = "D:/opencv3.1/opencv/build/etc/haarcascades/haarcascade_eye.xml";CascadeClassifier eye_detector;int main(int argc, char** argv) { Mat src = imread("D:/23-01.jpg"); if (src.empty()) { printf("could not load image...n"); return -1; } if (!eye_detector.load(filename)) { printf("could not load data file...n"); return -1; } Mat imgOut = src.clone(); Mat gray; vector<Rect> eyes; cvtColor(src, gray, COLOR_BGR2GRAY); equalizeHist(gray, gray); eye_detector.detectMultiScale(gray, eyes, 1.1, 3, 0, Size(100, 100)); for (size_t t = 0; t < eyes.size(); t++) { // 通道分离 Mat eye = src(eyes[t]); vector<Mat>bgr(3); split(eye, bgr); // 基于像素模型的红眼区域检测 Mat mask = (bgr[2] > 150) & (bgr[2] > (bgr[1] + bgr[0])); // 区域填充与提取 Mat mask_floodfill = mask.clone(); floodFill(mask_floodfill, cv::Point(0, 0), Scalar(255)); Mat mask2; bitwise_not(mask_floodfill, mask2); mask = (mask2 | mask); dilate(mask, mask, Mat(), Point(-1, -1), 3, 1, 1); if (t == 1) { imshow("mask", mask); } // 修复 Mat mean = (bgr[0] + bgr[1]) / 2; mean.copyTo(bgr[0], mask); mean.copyTo(bgr[1], mask); mean.copyTo(bgr[2], mask); // 回填 Mat eyeOut; merge(bgr, eyeOut); eyeOut.copyTo(imgOut(eyes[t])); rectangle(src, eyes[t], Scalar(0, 0, 255), 2, 8, 0); } imshow("input", src); imshow("output", imgOut); waitKey(0); return 0;}
总结:
整个处理就是考察使用OpenCV图像处理的基本操作能力、以及像素操作ROI区域与MASK的巧妙运用。基于OpenCV3.1.0完成全部代码调试。
- CodeM美团点评编程大赛初赛B轮 黑白树【DFS深搜+暴力】
- Uva 10339 - Watching Watches【数论,暴力】
- Codeforces 626E Simple Skewness(暴力枚举+二分)
- 如何启用Windows 10客户端Hyper-V
- 51Nod 1632 B君的连通(递归,快速幂)
- 51Nod 1046 A^B Mod C(日常复习快速幂)
- EntityFramework 外键值映射
- Codeforces 626C Block Towers(二分)
- 51Nod 1004 n^n的末位数字(日常复习快速幂,莫名的有毒,卡mod值)
- kmp模版
- 触发器在渗透中的利用
- Codeforces 626B Cards(模拟+规律)
- Android 5.0屏幕录制漏洞(CVE-2015-3878)威胁预警
- Codeforces 626A Robot Sequence(模拟)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法