目录

1 红眼消除

1.1 眼部检测

1.2 红眼遮掩

1.3 清除瞳孔掩模空洞

1.4 红眼修复

2 结果与完整代码

2.1 结果

2.2 代码

3 参考


在本教程中,我们将学习如何完全自动地从照片中消除红眼。如下图所示:

当我们晚上拍摄的照片有红眼效果时,带着血腥眼睛的微笑的人会让人想起德古拉。使用照片编辑工具可以删除红眼,但是需要很长的时间来学习。构建一个可用于各种图像的强大的红眼消除应用程序超出了本文的范围。但是,我们将学习基本原理并验证效果。

什么原因导致闪光灯拍照中的红眼效应?

当你在一个黑暗的房间里时,你的瞳孔会扩张(放大)以获得更多光线帮助你看得更清楚。大多数相机的闪光灯都非常靠近镜头。当您使用闪光灯拍摄照片时,来自闪光灯的光线会通过放大的瞳孔传到眼球的后部,然后通过瞳孔返回到相机的镜头中。眼球的后部称为眼底。由于眼底血液供应充足,它呈红色。

眼底的图像显示在下面。检查眼底可以揭示很多关于一个人的健康。您甚至可以获得智能手机应用程序,帮助您通过附件查看眼底。大多数相机持续闪烁几秒,使瞳孔收缩,从而减少红眼的可能性。

1 红眼消除

在本节中,我们将逐步介绍用于自动消除红眼的算法。

1.1 眼部检测

第一步是自动检测眼睛。我们使用标准的OpenCV Haar探测器(haarcascade_eye.xml)来寻找眼睛。有时,首先运行面部检测器然后检测面部区域内的眼睛是有意义的。为了简单起见,我们直接在图像上运行眼睛检测器。当输入图像是人像拍摄,或者您有眼睛的特写镜头时,跳过面部检测器。

您也可以按照此处的说明训练您自己的HAAR物体探测器。

https://www.learnopencv.com/training-better-haar-lbp-cascade-eye-detector-opencv

人眼检测模型调用代码如下:

C++:

// Read image
Mat img = imread("red_eyes.jpg",CV_LOAD_IMAGE_COLOR);// Output image
Mat imgOut = img.clone();// Load HAAR cascade
CascadeClassifier eyes_cascade("haarcascade_eye.xml");// A vector of Rect for storing bounding boxes for eyes.
std::vector<Rect> eyes;// Detect eyes.
eyesCascade.detectMultiScale( img, eyes, 1.3, 4, 0 | CASCADE_SCALE_IMAGE, Size(100, 100) );

Python:

# Read image
img = cv2.imread("red_eyes.jpg", cv2.IMREAD_COLOR)# Output image
imgOut = img.copy()# Load HAAR cascade
eyesCascade = cv2.CascadeClassifier("haarcascade_eye.xml")# Detect eyes
eyes = eyesCascade.detectMultiScale(img,scaleFactor=1.3, minNeighbors=4, minSize=(100, 100))

1.2 红眼遮掩

接下来,我们需要找到受红眼影响的瞳孔部分。有许多不同的方法可以找到红色的东西。需要注意的一点是,我们的颜色不仅仅是红色,而是鲜红色!您可以将图像转换为HSV色彩空间并根据色调和亮度设定阈值。在这篇文章中,我们使用了一个更简单的启发式方法。红色通道值大于阈值,阈值为绿色和蓝色通道的总和时,则判断当前颜色为红色。出于概念验证系统的目的,这个方法是足够的,但如果你想为商业软件包建立自动防红眼,你需要收集成千上万的红眼图像来提出更好的东西。

在下面的代码中,我们遍历我们在上一步中检测到的所有眼睛矩形。然后我们使用命令split将彩色图像分割成三个通道。最后对于每个像素点,如果红色通道高于阈值(150),且红色通道大于绿色和蓝色通道的总和,则该点值设为255,否则为0。则得到掩模图像,掩模图像中红眼位置为白色,其他位置为黑色。代码如下:

C++:

for( size_t i = 0; i &lt; eyes.size(); i++ )
{// Extract eye from the image.Mat eye = img(eyes[i]);// Split eye image into 3 channels.vector<Mat>bgr(3);split(eye,bgr);// Simple red eye detectorMat mask = (bgr[2] > 150) &amp; (bgr[2] &gt; ( bgr[1] + bgr[0] ));
}

Python:

for (x, y, w, h) in eyes:# Extract eye from the image.eye = img[y:y+h, x:x+w]# Split eye image into 3 channelsb = eye[:, :, 0]g = eye[:, :, 1]r = eye[:, :, 2]# Add the green and blue channels. bg = cv2.add(b, g)# Simple red eye detectormask = (r > 150) &  (r > bg)# Convert the mask to uint8 format. mask = mask.astype(np.uint8)*255

1.3 清除瞳孔掩模空洞

在上一步中创建的掩模中很可能会有洞。下图中的左图显示了使用颜色处理获得的原始掩模处理步骤。首先获得有孔洞的掩模,然后填充孔洞,最后扩充孔洞。此外,扩大面罩是一个好主意,因此它覆盖了稍大的区域。这是因为在边界处颜色逐渐消失,并且在我们的原始掩模中可能没有捕捉到一些红色区域。在下图中,右图是扩大的掩模。我们使用下面代码删除了掩码中的漏洞并生成扩张掩码。

代码如下:

C++:

void fillHoles(Mat &amp;mask)
{Mat mask_floodfill = mask.clone();floodFill(mask_floodfill, cv::Point(0,0), Scalar(255));Mat mask2;bitwise_not(mask_floodfill, mask2);mask = (mask2 | mask);}// Clean up mask by filling holes and dilating
fillHoles(mask);
dilate(mask, mask, Mat(), Point(-1, -1), 3, 1, 1);

Python:

def fillHoles(mask):maskFloodfill = mask.copy()h, w = maskFloodfill.shape[:2]maskTemp = np.zeros((h+2, w+2), np.uint8)cv2.floodFill(maskFloodfill, maskTemp, (0, 0), 255)mask2 = cv2.bitwise_not(maskFloodfill)return mask2 | mask
# Clean up mask by filling holes and dilating
mask = fillHoles(mask)
mask = cv2.dilate(mask, None, anchor=(-1, -1), iterations=3, borderType=1, borderValue=1)

1.4 红眼修复

现在我们有一个只包含每只眼睛红色区域的面具。我们接下来展示如何处理这个面具内的区域来修复红眼。

我们知道红眼睛会使图像中的红色通道饱和。换句话说,红色通道中的所有信息都被破坏。我们怎样才能恢复一些这些信息呢?修复红眼时,我们不需要在红色通道中检索真正的底层信息; 我们只需要找到合理的信息。幸运的是,红眼效果仅在红色通道中破坏眼球信息; 蓝色和绿色通道仍然很好。您可以在下图中看到图像的红色,绿色和蓝色通道下的眼球信。

可以使用绿色和蓝色通道的组合来提供合理的红色通道。例如,我们可以创建一个红色通道,它是图像中绿色和蓝色通道的平均值。然而,这样做可能会给眼球一点点色调,看起来不错,但不是很好。注意中心图像中间图像的紫色。

这给我们带来了一个重要的问题。瞳孔的颜色应该是多少?眼睛的内部是完全黑色的。因此,瞳孔应该是无色的(灰度)和黑暗的。我们不是仅替换瞳孔区域中的红色通道,而是用绿色和蓝色通道的平均值替换所有通道。这消除了紫色调。如上图第三张图像。最后一步是合并三个通道以创建RGB图像,然后将此固定的眼睛区域替换原始图像眼睛区域中。

代码如下:

C++:

// Calculate the mean channel by averaging
// the green and blue channels
Mat mean = (bgr[0]+bgr[1])/2;// Copy the mean image to blue channel with mask.
mean.copyTo(bgr[0], mask);// Copy the mean image to green channel with mask.
mean.copyTo(bgr[1], mask);// Copy the mean image to red channel with mask.
mean.copyTo(bgr[2], mask);
// Merge the three channels
Mat eyeOut;
merge(bgr,eyeOut);// Copy the fixed eye to the output image.
eyeOut.copyTo(imgOut(eyes[i]));

python:

# Calculate the mean channel by averaging
# the green and blue channels. Recall, bg = cv2.add(b, g)
mean = bg / 2
mask = mask.astype(np.bool)[:, :, np.newaxis]
mean = mean[:, :, np.newaxis]# Copy the eye from the original image.
eyeOut = eye.copy()# Copy the mean image to the output image.
np.copyto(eyeOut, mean, where=mask)
# Copy the fixed eye to the output image.
imgOut[y:y+h, x:x+w, :] = eyeOut

2 结果与完整代码

2.1 结果

我们首先在开始的示例图像上显示结果。结果如图所示:

请注意,从瞳孔区域移除所有颜色会使图像看起来很漂亮,因为眼睛中心的点是完全白色的。还要注意,在瞳孔的边界上,红色消失,但是由于扩张操作,我们仍然捕获该区域。

接下来,我们在眼睛的特写照片上显示结果,如下图所示。如果我们使用了人脸检测器,它就不会检测到人脸,并且自动眼睛检测器不会起作用。

2.2 代码

本文所有代码和人眼检测模型见:

https://github.com/luohenyueji/OpenCV-Practical-Exercise

完整代码如下:

C++:

#include "pch.h"
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;//孔洞填充
void fillHoles(Mat &mask)
{Mat maskFloodfill = mask.clone();//漫水填充floodFill(maskFloodfill, cv::Point(0, 0), Scalar(255));Mat mask2;//反色bitwise_not(maskFloodfill, mask2);//或运算mask = (mask2 | mask);
}int main()
{// Read image 读彩色图像Mat img = imread("./image/red_eyes.jpg", CV_LOAD_IMAGE_COLOR);// Output image 输出图像Mat imgOut = img.clone();// Load HAAR cascade 读取haar分类器CascadeClassifier eyesCascade("./model/haarcascade_eye.xml");// Detect eyes 检测眼睛std::vector<Rect> eyes;//前四个参数:输入图像,眼睛结果,表示每次图像尺寸减小的比例,表示每一个目标至少要被检测到4次才算是真的//后两个参数:0 | CASCADE_SCALE_IMAGE表示不同的检测模式,最小检测尺寸eyesCascade.detectMultiScale(img, eyes, 1.3, 4, 0 | CASCADE_SCALE_IMAGE, Size(100, 100));// For every detected eye 每只眼睛都进行处理for (size_t i = 0; i < eyes.size(); i++){// Extract eye from the image. 提取眼睛图像Mat eye = img(eyes[i]);// Split eye image into 3 channels. 颜色分离vector<Mat>bgr(3);split(eye, bgr);// Simple red eye detector 红眼检测器,获得结果掩模Mat mask = (bgr[2] > 150) & (bgr[2] > (bgr[1] + bgr[0]));// Clean mask 清理掩模//填充孔洞fillHoles(mask);//扩充孔洞dilate(mask, mask, Mat(), Point(-1, -1), 3, 1, 1);// Calculate the mean channel by averaging the green and blue channels//计算b通道和g通道的均值Mat mean = (bgr[0] + bgr[1]) / 2;//用该均值图像覆盖原图掩模部分图像mean.copyTo(bgr[2], mask);mean.copyTo(bgr[0], mask);mean.copyTo(bgr[1], mask);// Merge channelsMat eyeOut;//图像合并cv::merge(bgr, eyeOut);// Copy the fixed eye to the output image.// 眼部图像替换eyeOut.copyTo(imgOut(eyes[i]));}// Display Resultimshow("Red Eyes", img);imshow("Red Eyes Removed", imgOut);waitKey(0);return 0;
} 

python:


import cv2
import numpy as npdef fillHoles(mask):maskFloodfill = mask.copy()h, w = maskFloodfill.shape[:2]maskTemp = np.zeros((h+2, w+2), np.uint8)cv2.floodFill(maskFloodfill, maskTemp, (0, 0), 255)mask2 = cv2.bitwise_not(maskFloodfill)return mask2 | maskif __name__ == '__main__' :# Read imageimg = cv2.imread("./image/red_eyes.jpg", cv2.IMREAD_COLOR)# Output imageimgOut = img.copy()# Load HAAR cascadeeyesCascade = cv2.CascadeClassifier("./model/haarcascade_eye.xml")# Detect eyeseyes = eyesCascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors=4, minSize=(100, 100))# For every detected eyefor (x, y, w, h) in eyes:# Extract eye from the imageeye = img[y:y+h, x:x+w]# Split eye image into 3 channelsb = eye[:, :, 0]g = eye[:, :, 1]r = eye[:, :, 2]# Add the green and blue channels.bg = cv2.add(b, g)# Simple red eye detector.mask = (r > 150) &  (r > bg)# Convert the mask to uint8 format.mask = mask.astype(np.uint8)*255# Clean mask -- 1) File holes 2) Dilate (expand) mask.mask = fillHoles(mask)mask = cv2.dilate(mask, None, anchor=(-1, -1), iterations=3, borderType=1, borderValue=1)# Calculate the mean channel by averaging# the green and blue channelsmean = bg / 2mask = mask.astype(np.bool)[:, :, np.newaxis]mean = mean[:, :, np.newaxis]# Copy the eye from the original image.eyeOut = eye.copy()# Copy the mean image to the output image.#np.copyto(eyeOut, mean, where=mask)eyeOut = np.where(mask, mean, eyeOut)# Copy the fixed eye to the output image.imgOut[y:y+h, x:x+w, :] = eyeOut# Display Resultcv2.imshow('Red Eyes', img)cv2.imshow('Red Eyes Removed', imgOut)cv2.waitKey(0)

3 参考

https://www.learnopencv.com/automatic-red-eye-remover-using-opencv-cpp-python/

[OpenCV实战]29 使用OpenCV实现红眼自动去除相关推荐

  1. [OpenCV实战]47 基于OpenCV实现视觉显著性检测

    人类具有一种视觉注意机制,即当面对一个场景时,会选择性地忽略不感兴趣的区域,聚焦于感兴趣的区域.这些感兴趣的区域称为显著性区域.视觉显著性检测(Visual Saliency Detection,VS ...

  2. [OpenCV实战]24 使用OpenCV进行曝光融合

    目录 1 什么是曝光融合 2 曝光融合的原理 3 代码与结果 4 参考 本教程中,我们将了解使用OpenCV的Exposure Fusion(曝光融合). 1 什么是曝光融合 曝光融合是一种将使用不同 ...

  3. [OpenCV实战]39 在OpenCV中使用ArUco标记的增强现实

    文章目录 1 什么是ArUco标记? 2 在OpenCV中生成ArUco标记 3 检测Aruco标记 4 增强现实应用 5 总结和代码 5.1 生成aruco标记 5.2 使用aruco增强现实 6 ...

  4. [OpenCV实战]52 在OpenCV中使用颜色直方图

    颜色直方图是一种常见的图像特征,顾名思义颜色直方图就是用来反映图像颜色组成分布的直方图.颜色直方图的横轴表示像素值或像素值范围,纵轴表示该像素值范围内像素点的个数或出现频率.颜色直方图属于计算机视觉中 ...

  5. 实战:使用 OpenCV 的自动驾驶汽车车道检测(附代码)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 一.边缘检测 我们将使用 Canny 进行边缘检测.如果你不确定这 ...

  6. 使用OpenCV自动去除背景色

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 几天前,我遇到了一个项目,要求将草图放到某个文件夹中时删除草图的白 ...

  7. 实战:使用OpenCV+Python+dlib为人脸生成口罩

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|AI算法与图像处理 本文使用OpenCV dlib库生成口 ...

  8. 再次升级,985博士整理的71个OpenCV实战项目教程开放下载!

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  9. 基于OpenCV实战:提取中心线

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|AI算法与图像处理 问题 前几天有个人问了我一个问题,问 ...

最新文章

  1. Groovy 和 Gradle
  2. linux字符设备驱动架构,linux驱动开发--字符设备:cdd_cdev结构中private_data使用
  3. 简单而易忽视的http 404
  4. springMVC图片的上传与下载
  5. 游戏运营期间我的项目开发经验总结——纪律性和卡顿处理
  6. python批量跑plsql_python实现自动化报表(Oracle/plsql/Excel/多线程)
  7. c# bool?和bool_C#中的bool关键字
  8. 《Python入门到精通》流程控制语句
  9. webpack5的tree-shaking值得了解
  10. 从小白到大数据人工智能专家的学习历程-送100G学习资料
  11. 软件工程-软件测试和系统运维
  12. VMware Horizon view 7 云桌面终端安全解决方案
  13. ios系统怎么编辑html,word转html ios 可编辑
  14. 等保知识|云计算问题的通俗解释
  15. (5/8 软件漏洞攻击利用技术)如何成为一名黑客(网络安全从业者)——网络攻击技术篇
  16. 树莓派采集MPU9250运行AHRS进行姿态解算
  17. Beta阶段站立会议-02
  18. 搭载“鸿蒙”的华为Watch 3,是智能手表的标准答案吗?
  19. ffmpeg与ffdshow的关系
  20. 表单验证使用注解@Valid

热门文章

  1. 攻防世界——xff_referer
  2. 矩阵论笔记(四)——酉空间与酉变换
  3. 戒烟-这书能让你戒烟pdf
  4. python 字典列表,元组列表 列表嵌套字典 列表嵌套元组 字典嵌套列表
  5. java游戏张飞洗澡,张飞洗澡 刘备督战?在爆笑中搞懂电动汽车电池安全
  6. 【机器学习】模型评估与选择(实战)
  7. Rare-Variant Association Analysis | 罕见变异的关联分析
  8. jQuery实现一个学生成绩单录入系统
  9. 全国地理信息资源服务系统行政边界矢量数据下载教程
  10. 160429 vue.js 2 台灣小凡(体验 vuejs 2之随笔)