祛除红眼算法python实现

最近研究了一下祛除红眼的算法,主要的思想都是将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效果会平滑很多,没有明显的瑕疵。

-----------------------------------------------分割线----------------------------------------------

看了一下gimp也有去除红眼的算法,会更加简单,使用python也很容易实现,结果会更加好一些,而且阈值可以调整

def red_eye_removal(img, threshold=0.4):
    out_img = img.copy()
    assert 0.0 <= threshold <= 0.8
    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]
        eyeOut = eye.copy()
        adjusted_red = eye[:, :, 2]*0.5133333
        adjusted_green = eye[:, :, 1]*1.0
        adjusted_blue = eye[:, :, 0]*0.1933333
        adjusted_threshold = (threshold - 0.4) * 2
        flag1 = np.logical_and(adjusted_red >= adjusted_green - adjusted_threshold, True)
        flag2 = np.logical_and(adjusted_red >= adjusted_blue - adjusted_threshold, True)
        flag = np.logical_and(flag1, flag2)
        tmp = (adjusted_green + adjusted_blue) / (2.0 * 0.5133333)
        tmp = np.clip(tmp, 0.0, 1.0)
        eyeOut[:, :, 2] = np.where(flag, tmp, eye[:, :, 2])
        out_img[y:y + h, x:x + w, :] = eyeOut
    return out_img

猜你喜欢

转载自blog.csdn.net/zhou_438/article/details/131252388