꽤 전에 완성을 했었는데 블로그에 올린다는 것을 완전히 까먹고 있었다.
전에 얼굴 탐지 및 전처리, 데이터 크롤링 등등의 글들을 올렸었는데,
그 이후 조금의 삽질 과정이 있었고 결국 완성했다.
얼굴 인식도 잘 되고 모자이크 처리도 잘 된다. 하지만...
컴퓨터 성능이 안 좋아서 그런지, 랜드마크를 추출하는데 시간이 오래 걸려서 그런지
프레임이 매우 매우 낮다. 이 문제가 나중에 해결된다면 업데이트하겠다.
얼굴 인식은 아래와 같이 진행된다.
1. 데이터베이스에 인식하고 싶은 사람의 얼굴 랜드마크를 저장.
2. 실시간으로 사람 얼굴을 탐지하고 그 얼굴의 랜드마크를 추출.
3. 데이터베이스에 있는 랜드마크와 새로 추출된 랜드마크의 거리를 측정.
4. 제일 짧은 거리가 0.45 이하라면 그 사람으로 인식, 아니라면 모르는 사람이라고 인식.
5. 인식된 사람이라면 그대로 내버려 두고, 모르는 사람이라면 모자이크 처리를 함.
아래는 그 코드이다.
사용한 라이브러리)
# 라이브러리
import dlib, cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import ImageFont, ImageDraw, Image
import tensorflow.keras
from tensorflow.keras import backend as K
detector = dlib.get_frontal_face_detector()
sp = dlib.shape_predictor('models/shape_predictor_68_face_landmarks.dat')
facerec = dlib.face_recognition_model_v1('models/dlib_face_recognition_resnet_model_v1.dat')
함수들)
# 얼굴 탐지
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.int)
for k, d in enumerate(dets):
rect = ((d.left(), d.top()), (d.right(), d.bottom()))
rects.append(rect)
shape = sp(img, d)
# convert dlib shape to numpy array
for i in range(0, 68):
shapes_np[k][i] = (shape.part(i).x, shape.part(i).y)
shapes.append(shape)
return rects, shapes, shapes_np
# 랜드마크 추출
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)
데이터베이스 생성)
# 인식하고 싶은 사람들 이미지 지정
# 여기는 사용자가 원하는 대로 바꾸면 된다.
img_paths = {
'진호': 'user_img/jinho.jpg',
'태호': 'user_img/taeho.jpg',
'아이린': 'user_img/irene.jpg',
'조이': 'user_img/joy.jpeg',
'창모': 'user_img/changmo.jpg',
'홍철': 'user_img/hongchul.jpg',
'명수': 'user_img/myungsoo.jpg'
}
# 인식하고 싶은 사람들의 얼굴 랜드마크 추출후 저장
descs = []
for name, img_path in img_paths.items():
img = cv2.imread(img_path)
_, img_shapes, _ = find_faces(img)
descs.append([name, encode_faces(img, img_shapes)[0]])
np.save('user_img/descs.npy', descs)
print(descs)
실시간 얼굴 인식과 모자이크 처리)
cam = cv2.VideoCapture(0) # 노트북 웹캠 사용
cam.set(3, 640) # 너비
cam.set(4, 480) # 높이
font = cv2.FONT_HERSHEY_SIMPLEX # 폰트
while True:
ret, img = cam.read()
img = cv2.flip(img, 1) # 좌우 대칭
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
rects, shapes, _ = find_faces(img) # 얼굴 찾기
descriptors = encode_faces(img, shapes) # 인코딩
for i, desc in enumerate(descriptors):
x = rects[i][0][0] # 얼굴 X 좌표
y = rects[i][0][1] # 얼굴 Y 좌표
w = rects[i][1][1]-rects[i][0][1] # 얼굴 너비
h = rects[i][1][0]-rects[i][0][0] # 얼굴 높이
# 추출된 랜드마크와 데이터베이스의 랜드마크들 중 제일 짧은 거리를 찾는 부분
descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
dist = np.linalg.norm([desc] - descs1[0][1], axis=1)
if dist < 0.45: # 그 거리가 0.45보다 작다면 그 사람으로 판단
name = descs1[0][0]
else: # 0.45보다 크다면 모르는 사람으로 판단 -> 모자이크 처리
name = "누구?"
mosaic_img = cv2.resize(img[y:y+h, x:x+w], dsize=(0, 0), fx=0.04, fy=0.04) # 축소
mosaic_img = cv2.resize(mosaic_img, (w, h), interpolation=cv2.INTER_AREA) # 확대
img[y:y+h, x:x+w] = mosaic_img # 인식된 얼굴 영역 모자이크 처리
cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2) # 얼굴 영역 박스
cv2.putText(img, str(dist)[1:6], (x+5,y+h-5), font, 2, (0,0,255), 4) # 사진에 거리 출력
# 영어
# cv2.putText(img, name, (x+5,y-5), font, 2, (255,255,255), 4)
# 한글
img = Image.fromarray(img)
draw = ImageDraw.Draw(img)
draw.text((x+5,y-50), name, font=ImageFont.truetype("./batang.ttc", 60), fill=(255,255,255))
img = np.array(img)
cv2.imshow('camera', img)
k = cv2.waitKey(10) & 0xff # 'ESC' 키 누르면 종료
if k == 27:
break
cam.release()
cv2.destroyAllWindows()
실시간 X 그냥 얼굴 인식과 모자이크 처리)
img = cv2.imread('test_img/inf.jpg')
rects, shapes, _ = find_faces(img) # 얼굴 찾기
descriptors = encode_faces(img, shapes) # 인코딩
for i, desc in enumerate(descriptors):
x = rects[i][0][0] # 얼굴 X 좌표
y = rects[i][0][1] # 얼굴 Y 좌표
w = rects[i][1][1]-rects[i][0][1] # 얼굴 너비
h = rects[i][1][0]-rects[i][0][0] # 얼굴 높이
# 추출된 랜드마크와 데이터베이스의 랜드마크들 중 제일 짧은 거리를 찾는 부분
descs1 = sorted(descs, key=lambda x: np.linalg.norm([desc] - x[1]))
dist = np.linalg.norm([desc] - descs1[0][1], axis=1)
if dist < 0.45: # 그 거리가 0.45보다 작다면 그 사람으로 판단
name = descs1[0][0]
else: # 0.45보다 크다면 모르는 사람으로 판단 -> 모자이크 처리
name = "누구?"
mosaic_img = cv2.resize(img[y:y+h, x:x+w], dsize=(0, 0), fx=0.04, fy=0.04) # 축소
mosaic_img = cv2.resize(mosaic_img, (w, h), interpolation=cv2.INTER_AREA) # 확대
img[y:y+h, x:x+w] = mosaic_img # 인식된 얼굴 영역 모자이크 처리
cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2) # 얼굴 영역 박스
cv2.putText(img, str(dist)[1:6], (x+5,y+h+10), font, 1, (0,0,255), 4) # 사진에 거리 출력
# 영어
# cv2.putText(img, name, (x+5,y-5), font, 2, (255,255,255), 4)
# 한글
img = Image.fromarray(img)
draw = ImageDraw.Draw(img)
draw.text((x+5,y-30), name, font=ImageFont.truetype("./batang.ttc", 30), fill=(255,255,255))
img = np.array(img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
일단 모델 파일 3개를 아래 주소를 통해 다운로드해야 된다.
github.com/davisking/dlib-models/raw/master/shape_predictor_68_face_landmarks.dat.bz2
그리고 데이터베이스 생성 부분에서 본인의 파일 상황에 맞게 img_paths 안에 이름과 파일 경로를 바꿔줄 필요가 있다.
결과 사진)
데이터베이스에 박명수, 노홍철, 창모, 조이 등등의 사진이 있었는데 딱 지정된 사람의 얼굴만 인식하고
다른 사람들의 얼굴은 모드 모자이크 처리한 것을 볼 수 있다.
'Projects > 얼굴 인식과 모자이크 처리' 카테고리의 다른 글
0. OpenCV를 이용해 셀카 찍기 (0) | 2020.03.24 |
---|---|
3.2 OpenCV를 이용한 실시간 얼굴 모자이크 처리 feat by 럭키짱의 강건마 (0) | 2020.03.23 |
3.1 OpenCV를 이용한 실시간 얼굴 모자이크 처리 (0) | 2020.03.23 |
2. OpenCV를 이용한 실시간 얼굴 탐지 (0) | 2020.03.23 |
1. OpenCV를 이용한 얼굴 탐지 (0) | 2020.03.23 |