最近研究了一下祛除红眼的算法,主要的思想都是将RGB通道里面的R通道给想法设法的降低,其他的通道稍微进行变换就行,这里使用python运行了一下例子看看,
version2参考了代码:https://www.cnblogs.com/cpuimage/p/9000203.html
使用python进行了重新复现,这个算法的效果很不错,比起遍地都是的方法(version1)会好一些,version1方法会明显祛除不干净的,初步猜测是version1方法里面使用的简单粗暴的mask,这个mask应该不够准确和平滑,会导致后续的效果精度不足。
import math
import cv2
import numpy as np
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
def remove_red_eyes_version1(img):
imgOut = img.copy()
eyesCascade = cv2.CascadeClassifier("haarcascade_eye.xml")
eyes = eyesCascade.detectMultiScale(img, scaleFactor=1.3)
for (x, y, w, h) in eyes:
eye = img[y:y+h, x:x+w]
b = eye[:, :, 0]
g = eye[:, :, 1]
r = eye[:, :, 2]
bg = cv2.add(b, g)
mask = (r > 150) & (r > bg)
mask = mask.astype(np.uint8)
mask = fillHoles(mask)
mask = cv2.dilate(mask, None, anchor=(-1, -1), iterations=3)
mean = bg / 2
mask = mask.astype(np.bool)[:, :, np.newaxis]
mean = mean[:, :, np.newaxis]
eyeOut = eye.copy()
eyeOut = np.where(mask, mean, eyeOut)
imgOut[y:y+h, x:x+w, :] = eyeOut
return imgOut
def remove_red_eyes_version2(img_input, radius):
img_out = img_input.copy()
height, width = img_input.shape[:2]
eyesCascade = cv2.CascadeClassifier("haarcascade_eye.xml")
eyes = eyesCascade.detectMultiScale(img_input, scaleFactor=1.3)
img_input=img_input.astype(float)
for (x, y, w, h) in eyes:
center_x=x+w//2
center_y=y+h//2
'''
#因为这个眼睛检测算法精度不是很好,很可能存在误检测的情况,
这里只能先简单过滤一下,可以选择更好的眼睛检测算法
'''
if w*h<100*100:
continue
left = np.clip(int(center_x - radius), 0, width)
top = np.clip(int(center_y - radius), 0, height)
right = np.clip(int(center_x + radius), 0, width)
bottom = np.clip(int(center_y + radius), 0, height)
pow_radius = radius * radius
for y in range(top, bottom):
offset_y = y - center_y
for x in range(left, right):
offset_x = x - center_x
dis = offset_x*offset_x+offset_y*offset_y
if dis <= pow_radius:
red = img_input[y, x, 2]
green = img_input[y, x, 1]
blue = img_input[y, x, 0]
nrv = blue+green
if nrv < 1:
nrv = 1
if green > 1:
bluf = blue/green
else:
bluf = blue
bluf = max(0.5, min(1.5, math.sqrt(bluf)))#获取一个数值为0.5到1.5之间的数值
redq = red/(nrv*bluf)
if redq > 0.7:
powr = 1.775-(redq*0.75+0.25)#可以看到如果redq越大,powr就越小,也就是说如果红色分量越大,powr就越小
if powr < 0:
powr = 0
powr = powr*powr
powb = 0.5+0.5*powr#因为powr大于0,所以powb大于0.5
powg = 0.75+0.25*powr#因为powr大于0,所以powg大于0.75
img_out[y, x, 2] = powr*red+0.5
img_out[y, x, 1] = powg*green+0.5
img_out[y, x, 0] = powb*blue+0.5
return img_out
if __name__ == '__main__' :
img = cv2.imread("34.jpg", cv2.IMREAD_COLOR)
imgOut = remove_red_eyes_version1(img)
imgOut2=remove_red_eyes_version2(img, 20)
cv2.imwrite('imgOut.jpg',imgOut)
cv2.imwrite('imgOut2.jpg',imgOut2)
效果图:
我再放大一些可能会更加明显
我们可以清楚的看到,version2效果会平滑很多,没有明显的瑕疵。