2024. 2. 12. 01:44ㆍProject/Face DB 구축
원본 자료는 개인 정보 보호를 위해 과제 종료 후 삭제되었습니다.
블로그에 업로드된 모든 내용 자료는 진행 중 테스트를 위해 만든 별도의 복제본의 편집본입니다.
최종 사용된 코드 및 시트와 다를 수 있습니다.
아래에 작성된 내용은 과제 진행 중 본인이 직접 진행한 부분만 포함하였습니다.
얼굴 검출을 위한 라이브러리는 MTCNN과 Dlib 두가지를 고려하여 진행했지만 최종적으로 얼굴 정렬이 필요하여 해당 처리가 편리한 Dlib을 이용했다. 시간적 여유가 없고 필자의 숙련도가 낮아 모듈화와 예외처리를 진행하지 않았다.
두 라이브러리 사용 중 이미지에서 얼굴이 단 하나도 검출되지 않는 경우 에러가 지속적으로 발생했다!!
자체 피드백 1) 함수화를 적용하지 않아 코드의 가독성이 매우 떨어진다.
자체 피드백 2) 예외처리를 진행하지 않아 Dlib과 OpenCV 내장 함수들의 에러가 발생 시 마다 종료되었다.
자체 피드백 3) 에러의 이유를 직접 탐색, 애러가 발생한 경우의 사진을 null 처리하여 임시로 지우고 수작업으로 진행했다.
2)의 세부 사항은 다음과 같다.
- 이미지 정규화를 위해 CV를 통하여 이미지를 Slicing 할 때, Padding으로 지정하고 싶었던 부분, 특히 머리 위 정수리 근처에서 사진이 잘린 경우가 빈번하여 가장 많은 에러를 야기했다. 해당 문제는 교수님과 상의 후 정규화 규칙을 변경하여 해결했다. 해당 문제는 에러코드를 분석하며 이미지 편집 중 발생할 수 있는 에러를 차단해가며 발견했다. 정규화 규칙이 변경되기 전에는 슬라이싱 영역이 실제 이미지를 벗어나는 경우 이미지의 영역까지만 사용하도록 규제하고 얼굴의 중심을 기준으로 이미지의 비율을 유지하도록 하였다.
- Dlib을 통해서 얼굴 인식이 되지 않는 사진이 유의미한 갯수로 발견되었다. 이를 해결하기 위해 에러코드를 분석하였다. 얼굴 검출을 위한 Dlib 코드가 리스트 내포 반복문으로 반복되었는데 에러가 발생시(이미지가 검출되지 않을시) countinue 가 작동하고 해당 이미지의 이름을 출력하도록 설정하여 문제가 생긴 이미지를 따로 모아 문제사항을 발견하고 해당 이미지에 대해 mtcnn을 아용했다.
- 얼굴 검출 후 저장이 완료되기 전에 다른 이미지에서 얼굴을 검출하는, 매번 똑같이 재현되지는 않는 에러가 관찰되었다.
자기 반성을 하였으니 본론으로 들어가자. 실행 환경은 구글의 코랩을 이용하였다.
**주피터 노트북 형식으로 업로드하여 열람이 불가하다. 다운로드 또는 그 코드를 직접 열람하는 것을 추천한다.
[GitHub - GRAY-DDOT/family_face
Contribute to GRAY-DDOT/family_face development by creating an account on GitHub.
github.com](https://github.com/GRAY-DDOT/family_face)
1. 파일 준비
구글폼의 데이터를 구글 시트로 출력하여 이를 코랩에서 다루기 위해 위와 같은 코드를 실행했다.
from google.colab import auth
auth.authenticate\_user()
import gspread
from google.auth import default
creds, \_ = default()
gc = gspread.authorize(creds)
스프레드 또는 시트에 해당하는 주소를 입력한다. 이때 무엇의 url을 입력해야할지 모르겠다면, 스프레드의 첫번째 탭(시트)를 이용하고자 한다면 스프레드 대표 주소(파일의 주소), 또는 본인이 원하는 탭의 주소를 사용하면 된다.
이후로도 gspread 관련 언급이 있으니 추가적인 설명은 공식 문서를 참고하라.
[gspread 공식 문서](https://docs.gspread.org/en/v6.0.0/)
# 스프레드 주소
sheetUrl = "시트 URL"
# 시트에는 가족별 사진 제출 타입(제출 장 수), 사진 위치(디렉토리 또는 사진 URL https://drive.google.com/uc?id= 과 같이 다운로드 가능한 주소이어야함.)
sh = gc.open_by_url(sheetUrl)
#시트 접근 및 워크페이지 저장(0번째 시트 => 가족 정보 및 변환 url 저장)
info = sh.get_worksheet(0)
#시트 데이터 전부 불러오기
info_all = info.get_all_values()
#시트 데이터 판다스 데이터 프레임으로 변환
df_info_all = pd.DataFrame(info_all)
df_info_all = df_info_all.rename(columns={0:'family_ID', 1:'f_age', 2:'m_age',3:'c1_age',4:'c1_gender',5:'c2_age',6:'c2_gender',7:'c3_age',8:'c3_gender',9:'type',10:'3iP',11:'inF',12:'inM',13:'inC',14:'duo',15:'solo'})
df_info_all
2. 가족별 폴더 생성 및 json 작성
각 가족별로 ID를 부여하고 해당ID 명의 폴더로 가족별 사진을 구분하고 구성원 역할 별로 추가적인 ID를 부여하며 각 가족별로 구성원의 수, 총 사진 수량 등의 정보가 담긴 json 파일을 작성해야 했다.
#path_for_aligned
path= "저장할 디렉토리/"
f_id_list=df_info_all['family_ID'].tolist()
c1g_list = df_info_all['c1_gender'].tolist()
c2g_list = df_info_all['c2_gender'].tolist()
c3g_list = df_info_all['c3_gender'].tolist()
for i in f_id_list:
os.makedirs(path+'생성하고자 하는 디렉토리 이름 및 로직'+"/", exist_ok = True)
# JSON 작성
for i in f_id_list:
#자식 성별 로드
cg1 = c1g_list[int(i)-1]
cg2 = c2g_list[int(i)-1]
cg3 = c3g_list[int(i)-1]
# JSON 데이터 정의
if(cg2 == ''):
print("cg1 = " +cg1," \tcg2 = None"," \tcg3 = None")
jsonData = {
"family_id": 처리 로직,
"num_image": 3,
"num_family_member": 3,
"family_list":["F", "M", cg1 ]
}
elif(cg2 != ''):
if(cg3 == ''):
print("cg1 = " +cg1," \tcg2 = "+cg2," \tcg3 = None")
jsonData = {
"family_id": 처리 로직,
"num_image": 4,
"num_family_member": 4,
"family_list":["F", "M", cg1, cg2]
}
elif(cg3 != ''):
print("cg1 = " +cg1," \tcg2 = "+cg2," \tcg3 = "+cg3)
jsonData = {
"family_id": 처리 로직,
"num_image": 5,
"num_family_member": 5,
"family_list":["F", "M", cg1, cg2, cg3]
}
# JSON 파일로 저장
with open(path+"이름 작성 로직", 'w') as json_file:
json.dump(jsonData, json_file, indent=4)
3. 이미지 가공
## 이미지 디렉토리 조건 설정
img_type_list = df_info_all['type'].tolist()
#가족 id 순서대로 전달
#family_ID f_birth m_birth c_birth c_gender type solo_role 3iP inF inM inC duo solo
#개인 정보 보호를 위해 출력된 결과를 오류로 덮어씌움
#정상적인 결과 출력을 위해서는
#1.shape_predictor 수정
#2. 시트 데이터 로드
#3. 시트에 따른 코드 수정
for i in range(len(f_id_list)):
img_type = img_type_list[i]
print("=======================================================")
print("["+str(i)+"]")
print("img_type= "+img_type)
dir= []
print("sizeof dir= "+str(len(dir)))
if(img_type == 'a'):
dir.insert(0,df_info_all['3iP'][i])
print("Validated")
print(dir)
elif(img_type == 'c'):
dir.insert(0, df_info_all['duo'][i])
dir.insert(1, df_info_all['solo'][i])
print("Validated")
print(dir)
elif(img_type == 'b'):
dir.insert(0, df_info_all['inF'][i])
dir.insert(1, df_info_all['inM'][i])
dir.insert(2, df_info_all['inC'][i])
print("Validated")
print(dir)
elif(img_type == 'None'):
print("continue-i-img_type == 'None")
continue
else: print("expception: invalide img_type")
print("sizeof dir= "+str(len(dir)))
l=0
for j in dir:
print(j)
l+=1
if(img_type == 'None'):
print("[break-j-img_type == 'None]'")
break
img = url_to_image(j)
#aligned
dets = detector(img, 1)
# Find the 5 face landmarks we need to do the alignment.
faces = dlib.full_object_detections()
for detection in dets:
faces.append(predictor(img, detection))
# Get the aligned face images
# Optionally:
# images = dlib.get_face_chips(img, faces, size=160, padding=0.25)
images = dlib.get_face_chips(img, faces, size = 112)
for k in range(len(images)):
output_filename = path+"이름 작명 로직"+'.jpg'
cv2_imshow(images[k])
cv2.imwrite(output_filename, images[k])
위 코드를 진행할 때 크게 3가지의 이유로 런타임 오류가 발생했다.
1) 전달된 이미지에서 얼굴을 1개 이상 검출되지 않음 (예외가 발생하나 적절히 처리되지 않아 종료)
=> 원인을 추론할 수 있는 메세지가 출력됨. 검색을 통해 쉽게 찾을 수 있음
2) 이미지 슬라이싱 중 이미지 리스트 영역을 벗어난 연산이 수행된 경우
=> Dilb의 get_face_chips를 이용하도록 변경되어(aligned 조건 추가) 해결됨
3) 에러로 인해 저장소 reset(삭제) 후 dir 생성부터 진행할 경우, Google Drive에 변경 사항(삭제)이 적용되기 전 코드가 실행되어 누락, 중복 등의 문제가 발생
=> 저장소 삭제 후 1분 이상 대기하고 실행함
'Project > Face DB 구축' 카테고리의 다른 글
Projet[01]-Face DB 구축 [2] 응답 데이터 처리(구글 시트) (0) | 2023.12.03 |
---|---|
Projet[01]-Face DB 구축 [1] 제출 및 분석 수단 준비(구글 폼, 구글 시트) (0) | 2023.12.02 |