아카이브

[Opencv] opencv 연습 | 1. 인물 사진에서 얼굴 인식하기 본문

CS/CV

[Opencv] opencv 연습 | 1. 인물 사진에서 얼굴 인식하기

Rayi 2023. 1. 19. 22:00

다음 블로그의 글을 참고하였습니다.

https://seokii.tistory.com/112

 

[OpenCV with Python] - 15. 인물 사진 9000장 얼굴 인식 해보고 잘라서 저장해보기

코드 및 이미지 : https://github.com/Seokii/Study_OpenCV GitHub - Seokii/Study_OpenCV: study about OpenCV study about OpenCV. Contribute to Seokii/Study_OpenCV development by creating an account on GitHub. github.com https://seokii.tistory.com/111 [ML

seokii.tistory.com

* * *

Goal.

Opencv와 미리 학습된 모델을 이용해서 사진에서 인물의 얼굴을 표시해보겠습니다.

 

1. Dataset 준비

데이터셋으로는 KDDI AI Center 에서 제작한 LFW emotion dataset 을 사용했습니다.

아래 github 링크에서 내려받을 수 있습니다.

https://github.com/KDDI-AI-Center/LFW-emotion-dataset

 

GitHub - KDDI-AI-Center/LFW-emotion-dataset: Datasets released for <<Facial Expression Recognition with the advent of face masks

Datasets released for <<Facial Expression Recognition with the advent of face masks>> - GitHub - KDDI-AI-Center/LFW-emotion-dataset: Datasets released for <<Facial Expression Reco...

github.com

링크로 들어간 뒤 아래 표시한 링크를 누르면 내려받게 됩니다.

 

압축 해제한 뒤 LFW-emotion-dataset > data > LFW-FER > LFW-FER > train

경로로 들어가면 세 개의 이미지 폴더를 찾을 수 있습니다.

폴더 별로 각각 부정적 / 중립적 / 긍정적 표정인 얼굴의 이미지셋으로 분류되어 있습니다.

지금은 감정별 분류가 필요 없으므로 하나의 폴더로 합쳐서 사용하겠습니다.

 

2. Pre-trained Model 준비

이번에는 미리 학습된 모델을 사용합니다.

모델은 opencv에서 제공하는 haarcascades 모델을 사용하겠습니다.

아래 github 링크에서 내려받을 수 있습니다.

https://github.com/opencv/opencv/tree/master/data/haarcascades

 

GitHub - opencv/opencv: Open Source Computer Vision Library

Open Source Computer Vision Library. Contribute to opencv/opencv development by creating an account on GitHub.

github.com

링크로 들어가면 용도별로 여러가지의 xml 파일이 있는데

이중에서 정면 얼굴 인식용인 fontalface_default.xml 파일을 사용합니다.

 

3. 프로젝트 구성

Opencv-Py라는 이름의 프로젝트를 만들었습니다. 구성은 다음과 같습니다.

datasets

 ㄴ LFW : 하나의 폴더로 합쳤던 LFW dataset

models

 ㄴ haarcascade_frontalface_default.xml : 학습된 xml 파일 모델

outputs

 ㄴ LFW : 출력 이미지를 저장할 곳

FLW.py : 메인 코드

test.py : opencv 작동 테스트용 파일

readme.md : readme 파일

 

4. FLW.py

위 과정을 토대로 dataset의 이미지 파일들에서 얼굴을 표시하여

새로운 이미지 파일로 저장하는 코드를 작성했습니다.

 

4.1. import

 

1
2
import cv2
import os
cs

 

cv2는 opencv 라이브러리입니다.

os는 프로젝트 폴더의 경로들을 다루기 위해 사용합니다.

 

4.2. make_dataset_name_list( )

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# make_dataset_name_list(path) ==============================
# path (string) : directory of the dataset
# return (list) - list of name without extension
# ===========================================================
def make_dataset_name_list(path):
    name_list = os.listdir(path_dir)
    print(name_list[0])  # 'Aaron_Eckhart_0001.jpg'
    print(len(name_list))  # 9330
 
    name_list_nonex = []
    for i in range(len(name_list)):
        name_list_nonex.append(name_list[i].replace(".jpg"""))
    print(name_list_nonex[0])  # 'Aaron_Eckhart_0001'
    
    return name_list_nonex
cs

 

os.listdir(path) : path에 있는 모든 파일들의 이름을 list 형태로 반환합니다.

dataset의 이미지 파일들은 모두 "이름.jpg" 형태의 이름으로 되어 있습니다.

여기서 확장자를 떼고 "이름"의 형태로 바꾼 리스트를 만들어 반환하는 함수입니다.

 

4.3. find_face( )

 

1
2
3
4
5
6
7
8
9
# find_face(image, model) ===================================
# image (ndarray) : image array
# model (string) : directory of the xml file
# return (ndarray) - [[x, y, w, h], ...]
# ===========================================================
def find_face(image, model):
    face_cascade = cv2.CascadeClassifier(model)
    image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return face_cascade.detectMultiScale(image_gray, 1.35)
cs

 

CascadeClassifier( )는 opencv에서 제공하는 class입니다.

CascadeClassifier( )의 detectMultiScale( ) method를 이용해 이미지에서 얼굴을 찾습니다.

detectMultiScale(image, scaleFactor, minNeighbors)
image ndarray 대상 이미지
scaleFactor float 표시할 사각형의 크기( > 1.0). 증가할 수록 축소됨
minNeighbors int 근처 위치에도 사각형을 생성했을 대 목표(얼굴)로 인정되는 사각형의 최소 개수.
증가할 수록 목표(얼굴)를 인식하는 기준이 높아짐
Return ndarray 사각형 정보[x좌표, y좌표, 너비, 높이]에 대한 list

 

4.4. save_rect( )

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# save_rect(image, rects, show) =============================
# image (ndarray) : image array
# rects (ndarray) : captured rect arrays [[x, y, w, h], ...]
# show (bool) : if show output image (=False)
# return (None)
# ===========================================================
def save_rect(image, rects, show=False):
    for i in range(len(rects)):
        x, y, w, h = rects[i]
        cv2.rectangle(image, (x, y), (x+w, y+h), (25500), 2)
        cv2.imwrite(f"outputs/LFW/{name}_{i}.jpg", image)
        
        if show:
            cv2.imshow("face_recongnition", image)
            cv2.waitKey(0)
            cv2.destroyAllWindows()
cs

 

사각형 위치에 대한 정보를 받아 이미지 위에 표시한 뒤 경로를 지정해 저장합니다.

이미지의 이름을 "이미지 이름_번호.jpg"로 저장하는데, 이는 한 사진에 얼굴이 두 개 이상일 경우를 위해서 입니다.

가령 사람 두 명이 있는 'a'라는 이미지 파일이 있으면 'a_0', 'a_1'로 저장되는 식입니다.

 

추가로 show 값을 조정하여 실행 후 이미지를 출력할 수 있습니다.

waitKey(time)는 키보드에서 키입력이 있을 때까지 대기하는 함수입니다.

waitKey(time)
time int 대기 시간(ms)
0 : 무한 대기
Return str 입력한 키의 문자와 동일한 아스키 코드 값

 

4.5. 메인 코드

 

1
2
3
4
5
6
7
8
9
path_dir = "datasets/LFW/"
haarcascade = "models/haarcascade_frontalface_default.xml"
 
image_name_list = make_dataset_name_list(path_dir)
 
for name in image_name_list[:3]:
    face = cv2.imread("datasets/LFW/" + name + ".jpg")
    face_rects = find_face(face, haarcascade)  #[[x y w h]]
    save_rect(face, face_rects, show=True)
cs

 

경로를 나타내는 문자열은 미리 변수로 저장해둡니다.

FLW dataset에서부터 확장자를 뗀 이름 리스트 image_name_list를 생성합니다.

이후 순서대로 이미지 파일을 불러 와 find_face( )로 사각형을 찾습니다.

마지막으로 save_rect( )로 이미지에 얼굴 영역을 표시하여 저장 및 출력합니다.

 

파일 개수가 매우 많으니 간단하게 3개 까지만 실행하도록 했습니다.

 

5. 결과

다음과 같이 올바르게 얼굴이 인식되고 저장되었습니다.

 

 

아래는 코드 전문입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import cv2
import os
 
# make_dataset_name_list(path) ==============================
# path (string) : directory of the dataset
# return (list) - list of name without extension
# ===========================================================
def make_dataset_name_list(path):
    name_list = os.listdir(path_dir)
    print(name_list[0])  # 'Aaron_Eckhart_0001.jpg'
    print(len(name_list))  # 9330
 
    name_list_nonex = []
    for i in range(len(name_list)):
        name_list_nonex.append(name_list[i].replace(".jpg"""))
    print(name_list_nonex[0])  # 'Aaron_Eckhart_0001'
    
    return name_list_nonex
 
# find_face(image, model) ===================================
# image (ndarray) : image array
# model (string) : directory of the xml file
# return (ndarray) - [[x, y, w, h], ...]
# ===========================================================
def find_face(image, model):
    face_cascade = cv2.CascadeClassifier(model)
    image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return face_cascade.detectMultiScale(image_gray, 1.35)
 
# save_rect(image, rects, show) =============================
# image (ndarray) : image array
# rects (ndarray) : captured rect arrays [[x, y, w, h], ...]
# show (bool) : if show output image (=False)
# return (None)
# ===========================================================
def save_rect(image, rects, show=False):
    for i in range(len(rects)):
        x, y, w, h = rects[i]
        cv2.rectangle(image, (x, y), (x+w, y+h), (25500), 2)
        cv2.imwrite(f"outputs/LFW/{name}_{i}.jpg", image)
        
        if show:
            cv2.imshow("face_recongnition", image)
            cv2.waitKey(0)
            cv2.destroyAllWindows()
 
 
path_dir = "datasets/LFW/"
haarcascade = "models/haarcascade_frontalface_default.xml"
 
image_name_list = make_dataset_name_list(path_dir)
 
for name in image_name_list[:3]:
    face = cv2.imread("datasets/LFW/" + name + ".jpg")
    face_rects = find_face(face, haarcascade)  #[[x y w h]]
    save_rect(face, face_rects, show=False)
cs

 

728x90

'CS > CV' 카테고리의 다른 글

[Opencv] Opencv 설치하기  (0) 2023.01.13
Comments