1. 程式人生 > >【Python】使用openCV與dlib實現人臉68特徵點的檢測與手動修改

【Python】使用openCV與dlib實現人臉68特徵點的檢測與手動修改

在使用plib檢測人臉特徵點的過程中由於各種原因難免會遇到特徵點定位不準確的情況。這時,如果能夠手動修改來移動特徵點的位置,後續工作則可以更好地得以完成。
由於openCV的畫圖工具會覆蓋原來的圖片,這裡我通過手動儲存被修改畫素的辦法實現移動定位點而不損害原來影象。

程式碼思路:先獲得每個landmark的座標,將座標對應的點的畫素(一個7 * 7 * 3的array矩陣)儲存在IniPointColor 陣列中。
在滑鼠左鍵單擊時,如果單擊位置與某個點足夠接近,那麼該點被選中,LButtonFirstActionMovingPoint 設為真。
滑鼠移動時,若LButtonFirstAction

為真,則將IniPointColor 中對應點的畫素覆蓋移動前位置的畫素,否則庸PreviousPointColor 中的數值覆蓋。之後再將新點的畫素值儲存在PreviousPointColor 中,並在新的位置繪製點。

import sys
import cv2
import dlib
import numpy as np

#定義全域性變數
LButtonFirstAction = False
MovingPoint = False
PreviousPosition = (-1, -1)
PreviousPointColor = np.zeros([7, 7, 3])
MovingPNum = -1
IniPointColor = np.zeros([68, 7, 7, 3]) #獲取訓練模型 predictor_path = "shape_predictor_68_face_landmarks.dat" #使用官方提供的模型構建特徵提取器 predictor = dlib.shape_predictor(predictor_path) #使用dlib自帶的frontal_face_detector作為人臉檢測器 detector = dlib.get_frontal_face_detector()
#返回檢測框邊緣座標與人臉特徵點矩陣(68*2)
def get_landmarks
(img, predictor, detector):
rects = detector(img, 1) edge = [(rects[0].left(), rects[0].top()), (rects[0].right(), rects[0].bottom())] return edge, [(p.x, p.y) for p in predictor(img, rects[0]).parts()]

定義滑鼠事件的響應函式。

def on_mouse(event, x, y, flags, marks):
    #全域性變數宣告
    global LButtonFirstAction
    global MovingPNum
    global MovingPoint
    global PreviousPosition
    global PreviousPointColor
    global IniPointColor

    # 滑鼠左鍵按下
    if event == cv2.EVENT_LBUTTONDOWN:
        LButtonFirstAction = True
        for i in range(68):
            if np.sqrt(np.square(x-marks[i][0]) + np.square(y-marks[i][1])) < 10:
                MovingPNum = i
                MovingPoint = True
                print("Moving point %d" %i)

    #滑鼠左鍵擡起
    elif event == cv2.EVENT_LBUTTONUP:
        if MovingPoint:
            IniPointColor[MovingPNum] = PreviousPointColor
            print("Point {} was moved to location{}".format(MovingPNum, (x, y)))
            MovingPNum = -1
            MovingPoint = False

    # 滑鼠移動
    elif event == cv2.EVENT_MOUSEMOVE:
        if MovingPoint and MovingPNum != -1:
            if LButtonFirstAction:
                PreviousPointColor = IniPointColor[MovingPNum]
                LButtonFirstAction = False
            PreviousPosition = marks[MovingPNum]
            marks[MovingPNum] = (x,y)
            img[PreviousPosition[1]-3:PreviousPosition[1]+4,
                PreviousPosition[0]-3:PreviousPosition[0]+4, :] = PreviousPointColor.copy()

            PreviousPointColor = img[ y-3:y+4, x-3:x+4, :].copy()
            img[ y-3:y+4, x-3:x+4, :] = np.zeros([7, 7, 3])

主函式

for f in sys.argv[1:]:
    print("Processing file: {}".format(f))
    img = cv2.imread(f)
    edge, marks = get_landmarks(img, predictor, detector)
    cv2.rectangle(img, edge[0], edge[1], (255, 255, 255), thickness = 2)
    #計數器
    i = 0
    for p in marks:
        IniPointColor[i] = img[ p[1]-3:p[1]+4, p[0]-3:p[0]+4,:].copy()
        img[ p[1]-3:p[1]+4, p[0]-3:p[0]+4,:] = np.zeros([7, 7, 3])
        i += 1
    cv2.namedWindow(f,cv2.WINDOW_AUTOSIZE)
    #呼叫滑鼠響應事件
    cv2.setMouseCallback(f, on_mouse, marks)
    while(1):
        cv2.imshow(f,img)
        if cv2.waitKey(20)&0xFF==27:
            break
    cv2.destroyAllWindows()

已知問題:在兩個點距離較近時有可能有兩個點同時出現在滑鼠單擊的響應範圍內。這時,程式會選擇靠後的landmark作為被移動的物件。