강의자료/머신러닝

[머신러닝활용] CCTV속 범인얼굴 특정하기

파아란 기쁨 2023. 3. 7. 09:03

참고 소스 : https://github.com/kairess/simple_face_recognition

 

GitHub - kairess/simple_face_recognition

Contribute to kairess/simple_face_recognition development by creating an account on GitHub.

github.com

모델 다운로드 

shape_predictor_68_face_landmarks.dat - http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

dlib_face_recognition_resnet_model_v1.dat - github.com/kairess/simple_face_recognition/raw/master/models/dlib_face_recognition_resnet_model_v1.dat

 

이번에 만들어 볼 프로그램은 동영상 파일에서 특정 인물을 찾는 프로그램입니다.

만약 범인의 얼굴을 기록해 두고 CCTV 속 동영상에서 해당 얼굴과 매칭이 된다면 알람을 띄워준다거나 하면 범인을 잡을 때 사용할 수 있을 것 같습니다.

 

1. 동영상에서 얼굴 이미지 추출하기

1. 라이브러리 임포트

먼저 dlib,cv2 를 임포트 합니다.

import dlib, cv2,sys
import numpy as np


detector = dlib.get_frontal_face_detector() #이미지에서 얼굴을 검출
sp = dlib.shape_predictor('models/shape_predictor_68_face_landmarks.dat') #얼굴에서 68개의 특징점을 찾는 알고리즘
facerec = dlib.face_recognition_model_v1('models/dlib_face_recognition_resnet_model_v1.dat') #얼굴 인식 모델

이미지에서 얼굴위치를 검출 할 수 있도록 dlib.get_frontal_face_detector() 를 사용합니다.

또한 shape_predictor_68_face_landmarks.dat 을 이용하여 다음과 같이 68개의 특징점을 찾을 수 있습니다.

dlib_face_recognition_resnet_model_v1.dat 은 찾은 얼굴을 넘파이 배열 형태의 벡터로 변환하기 위해 사용합니다.

벡터 형태로 변환 하는 이유는 동영상에서 찾으려고 하는 이미지와 같은 특징을 찾았는지 체크 하기 위해서입니다.

 

2. 이미지에서 얼굴을 찾는 함수 만들기

한 프레임의 이미지가 들어 오면 얼굴을 찾아서 얼굴의 랜드마크 68개를 리스트에 담아서 리턴하는 함수입니다.

#얼굴을 찾는 함수
def find_faces(img):
  dets = detector(img,1)

  if len(dets)==0: #얼굴을 찾지 못하면
    return np.empty(0),np.empty(0),np.empty(0) #빈 배열 반환

  rects,shapes=[],[] #얼굴의 사각형 범위,특징을 담을 리스트 생성
  shapes_np = np.zeros((len(dets),68,2),dtype=np.int32) #얼굴의 랜드마크 68개의 점을 구해서 저장할 공간 얼굴개수 * 68 * 2(x,y)

  for k,d in enumerate(dets):
    rect = ((d.left(),d.top),(d.right(),d.bottom())) #얼굴의 좌상,우하 위치
    rects.append(rect) #리스트에 얼굴을 찾은 위치를 추가

    shape = sp(img,d) #이미지에서 68개의 특징점을 찾는다.

    for i in range(0,68):
      shapes_np[k][i]=(shape.part(i).x,shape.part(i).y) #특징점의 x,y를 shapes_np 에 셋팅

    shapes.append(shape)

  return rects,shapes,shapes_np #찾은 얼굴의 사각형,특징,랜드마크의 위치

rects 에는 찾은 얼굴의 사각형 영역이 리스트로 저장되고 shapes는 68개의 특징이 리스트에 저장 되고 shapes_np 에는 특징의 x,y축의 위치 정보가 저장되어 리턴 됩니다.

3. 찾은 얼굴을 128개의 벡터 데이터로 인코딩 하기

find_fac 에서 랜드마크를 찾은 부분을 128개의 벡터값으로 변환하는 함수를 구현해 봅니다.

이렇게 구현한 함수는 동영상에서 찾고자 하는 사람의 얼굴과 같은 사람인지 아닌지 판별할 때 사용합니다.

#얼굴을 인코딩하는 함수 이미지를 가지고 128개의 벡터로 만들어 준다.
#128개의 벡터를 가지고 그 사람이 같은 사람인지 아닌지를 판단하기 위함
def encode_faces(img,shapes):
  face_descriptors = []
  for shape in shapes:
    face_descriptor = facerec.compute_face_descriptor(img,shape)
    face_descriptors.append(np.array(face_descriptor))

  return np.array(face_descriptors)

compute_face_descriptor 은 정렬된 얼굴을 ResNet 모델로 전달하면 128차원 벡터로 얼굴을 표현 하는 메서드 입니다.

이렇게 나온 결과는 np.array 를 이용하여 넘파이 배열로 쉽게 바꿀 수 있습니다.

4. 찾을 사람의 이미지의 특징을 인코딩 하여 넘파이 형태로 저장합니다.

#찾을 사람의 이미지
img_paths={
  'hong':'img\\HONG.png'
}

descs={
  'hong':None
}

#img_path 에 있는 이미지에서 얼굴을 찾아서 찾은 값을 인코딩 하자.
for name,img_path in img_paths.items():
  img_bgr = cv2.imread(img_path)
  img_rgb = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2RGB) #bgr 형식의 이미지를 rgb 형태로 변환한다.

  _,img_shapes,_=find_faces(img_rgb) #얼굴의 특징을 img_shapes 리스트에 담는다.
  descs[name]=encode_faces(img_rgb,img_shapes)[0] #각각의 이름에 해당하는 특징을 128개의 벡터로 변환해서 저장

찾을 사람의 이미지를 img_paths에 여러명을 등록하면 여러명을 동시에 찾을 수 있습니다.

cv2.imread 로 이미지를 읽었을 때 bgr 형태로 로딩이 되는데 이것을 rgb 형태로 변환하여 얼굴의 특징점을 찾은 데이터를 인코딩하여 descs 딕셔너리에 해당 이름 값으로 저장하였습니다.

2. 웹캠으로 들어 오는 영상에서 찾고자 하는 얼굴이 있다면 표시를 봅니다.

cap = cv2.VideoCapture(0)

if not cap.isOpened():
  exit()

_, img_bgr = cap.read() # (800, 1920, 3)
padding_size = 0
resized_width = 320 #1920
video_size = (resized_width, int(img_bgr.shape[0] * resized_width // img_bgr.shape[1]))

while True:
  ret, img_bgr = cap.read()
  if not ret:
    break

  img_bgr = cv2.resize(img_bgr, video_size)
  img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
  
  dets = detector(img_bgr) #캡쳐한 이미지에서 얼굴을 찾는다.

  for k, d in enumerate(dets): #찾은 얼굴의 갯수만큼 반복하면서 정보를 d로 꺼낸다.
    shape = sp(img_rgb, d) #얼굴에서 68개의 특징점을 찾는다.
    face_descriptor = facerec.compute_face_descriptor(img_rgb, shape) # 이미지를 가지고 128개의 벡터로 만들어 준다

    last_found = {'name': 'unknown', 'dist': 0.6, 'color': (0,0,255)}

    for name, saved_desc in descs.items(): #찾을 얼굴의 벡터를 가지고 와서
      dist = np.linalg.norm([face_descriptor] - saved_desc, axis=1) #현재 얼굴과 찾을 얼굴의 벡터 거리를 찾는다.

      if dist < last_found['dist']: #만약 거리가 0.6보다 작다면 그 사람이다.
        last_found = {'name': name, 'dist': dist, 'color': (255,0,0)}

    cv2.rectangle(img_bgr, pt1=(d.left(), d.top()), pt2=(d.right(), d.bottom()), color=last_found['color'], thickness=2)
    cv2.putText(img_bgr, last_found['name'], org=(d.left(), d.top()), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=last_found['color'], thickness=2)

  cv2.imshow('img', img_bgr)
  if cv2.waitKey(1) == ord('q'):
    break

cap.release()

Detctor : 얼굴영역을 인식하여 dets 에 좌표 정보가 들어 간다.

dist = np.linalg.norm([face_descriptor] - saved_desc, axis=1) : 찾을 얼굴과 현재 동영상의 얼굴의 벡터의 유클리드 거리를 비교하여 차이가 0.6보다 작다면 찾고자 하는 사람이다.

 

실행 화면

 

 

 

사업자 정보 표시
원당컴퓨터학원 | 기희경 | 인천 서구 당하동 1028-2 장원프라자 502호 | 사업자 등록번호 : 301-96-83080 | TEL : 032-565-5497 | Mail : icon001@naver.com | 통신판매신고번호 : 호 | 사이버몰의 이용약관 바로가기