通过dlib人脸识别矫正旋转身份证图像

原理

        通过不停地旋转图片,直到dlib能够识别到人脸;通过dlib找到眼角、嘴角、鼻子、下巴特征点,假设鼻子到下巴的直线与双眼直线、嘴角直线垂直,找到脸部角度,结合已旋转角度,计算出最终图像旋转角度。

效果

 通过dlib人脸识别矫正旋转身份证图像_第1张图片

 代码

import dlib
import cv2
import numpy as np
import math
import os

detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
POINTS_NUM_LANDMARK = 68

def rotate_img(image, angle):
    # grab the dimensions of the image and then determine the
    # center
    (h, w) = image.shape[:2]
    (cX, cY) = (w // 2, h // 2)

    # grab the rotation matrix (applying the negative of the
    # angle to rotate clockwise), then grab the sine and cosine
    # (i.e., the rotation components of the matrix)
    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    # compute the new bounding dimensions of the image
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    # adjust the rotation matrix to take into account translation
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY

    # perform the actual rotation and return the image
    return cv2.warpAffine(image, M, (nW, nH))

def show(img, ms=0):
    """ 显示 """
    cv2.imshow('show', img)
    h, w = img.shape[:2]
    cv2.resizeWindow("show", w, h)
    cv2.waitKey(ms)

def _largest_face(dets):
    if len(dets) == 1:
        return 0

    face_areas = [(det.right() - det.left()) * (det.bottom() - det.top()) for det in dets]

    largest_area = face_areas[0]
    largest_index = 0
    for index in range(1, len(dets)):
        if face_areas[index] > largest_area:
            largest_index = index
            largest_area = face_areas[index]

    print("largest_face index is {} in {} faces".format(largest_index, len(dets)))

    return largest_index


# 从dlib的检测结果抽取姿态估计需要的点坐标
def get_image_points_from_landmark_shape(landmark_shape):
    if landmark_shape.num_parts != POINTS_NUM_LANDMARK:
        print("ERROR:landmark_shape.num_parts-{}".format(landmark_shape.num_parts))
        return -1, None

    # 2D image points. If you change the image, you need to change vector
    image_points = np.array([
        (landmark_shape.part(30).x, landmark_shape.part(30).y),  # Nose tip
        (landmark_shape.part(8).x, landmark_shape.part(8).y),  # Chin
        (landmark_shape.part(36).x, landmark_shape.part(36).y),  # Left eye left corner
        (landmark_shape.part(45).x, landmark_shape.part(45).y),  # Right eye right corne
        (landmark_shape.part(48).x, landmark_shape.part(48).y),  # Left Mouth corner
        (landmark_shape.part(54).x, landmark_shape.part(54).y)  # Right mouth corner
    ], dtype="double")

    return 0, image_points


# 计算两个点形成的直线角度,注意opencv的Y轴是朝下生长的,采用 y1-y2/x2-x1
# 最终结果在(-180 ~ 180 )
def computerAngle2(x1,y1,x2,y2):
    angle = math.atan2((y1 - y2), (x2 - x1))
    angle = angle * 180 / math.pi #(-180 ~ 180 )
    return angle


# 用dlib检测关键点,返回姿态估计需要的几个点坐标
def detectImage(img,dets,angle):
    largest_index = _largest_face(dets)
    face_rectangle = dets[largest_index]

    landmark_shape = predictor(img, face_rectangle)

    return get_image_points_from_landmark_shape(landmark_shape)

def findRotete(image_path):
    img = cv2.imread(image_path)
    finallyImg = None
    finallyAngle = 0
    for angle in range(0, 360, 60): # 不停旋转,注意如果每次旋转角度过大,存在所有角度无法识别到人脸的可能
        rotateImg = rotate_img(img, angle)
        dets = detector(rotateImg, 0)
        if (len(dets) > 0):
            finallyImg = rotateImg
            ret, image_points = detectImage(rotateImg, dets, angle)
            if ret != 0:
                print('get_image_points failed')
                break
            for p in image_points:
                cv2.circle(rotateImg, (int(p[0]), int(p[1])), 3, (0, 0, 255), -1)
            Nose_x = image_points[0][0]
            Nose_y = image_points[0][1]
            Chin_x = image_points[1][0]
            Chin_y = image_points[1][1]
            Left_Eye_x = image_points[2][0]
            Left_Eye_y = image_points[2][1]
            Right_Eye_x = image_points[3][0]
            Right_Eye_y = image_points[3][1]
            Left_Mouth_x = image_points[4][0]
            Left_Mouth_y = image_points[4][1]
            Right_Mouth_x = image_points[5][0]
            Right_Mouth_y = image_points[5][1]

            eyeAngle = computerAngle2(Left_Eye_x, Left_Eye_y, Right_Eye_x, Right_Eye_y)
            mouthAngle = computerAngle2(Left_Mouth_x, Left_Mouth_y, Right_Mouth_x, Right_Mouth_y)
            nose2chinAngle = computerAngle2(Nose_x, Nose_y, Chin_x, Chin_y)
            # 120.8157057517292 120.25643716352927 32.9052429229879
            nose2chinAngle += 90 # 假设鼻子到下巴的直线与双眼直线垂直
            print(angle, eyeAngle, mouthAngle, nose2chinAngle)
            avgAngle = (eyeAngle + mouthAngle + nose2chinAngle) / 3 # 即双眼直线、嘴角直线与鼻子到下巴直线(垂直后)这三条直线的平均角度,后续要将此角度归0,即直线平行于图片

            print(avgAngle)
            finallyAngle = angle + avgAngle  # (angle - (- avgAngle)) avgAngle需要归零,即-avgAngle, 又因为angle 是顺时针旋转角度,avgAngle是逆时针旋转角度,即需要减去(-avgAngle)
            if (finallyAngle > 360):
                finallyAngle -= 360
            if (finallyAngle < - 360):
                finallyAngle += 360
            print(finallyAngle)
            break
        show(rotate_img(img, angle), 200)
    if (rotateImg is not None):
        show(rotateImg, 1000)
        finallyImg = rotate_img(img, finallyAngle)
        filenames = image_path.split('.')
        filename = filenames[0] + "Modify." + filenames[1]
        print(filename)
        cv2.imwrite(filename, finallyImg)
        show(finallyImg, 2000)

dir = "rotateCard"
for s in os.listdir(dir):
    image_path = os.path.join(dir, s)
    if 'Modify' not in image_path:
        findRotete(image_path)

参考来源

opencv人脸检测,旋转处理 - 姜小豆 - 博客园 (cnblogs.com)

dlib【正面人脸检测】【特征点检测】【旋转】_dlib检测正脸_ya鸡给给的博客-CSDN博客

使用opencv和dlib进行人脸姿态估计(python)_dlib 人脸角度_yuanlulu的博客-CSDN博客

你可能感兴趣的:(图像矫正,计算机视觉,python,opencv)