0. 사용 이유
- 프로젝트에 얼굴인식을 수행해 대상의 신원, 나이, 성별을 인식하는 기능을 sk open api를 사용하여 구현이 완료된 상태입니다.
- 하지만 api 호출을 최소화 하기위해 이미지의 유효성검사를 자체적으로 실행하여 개선 목적으로 ProcessBuilder로 유효성 검사 실행을 도전했습니다.
- 성능 비교를위해 다음과 같이 임시 test를 진행해보겠습니다.
1. ProcessBuilder란?
- Process 클래스의 보조 클래스로 start 메서드를 호출하면서ProcessBuilder 클래스의 인스턴스에 정의 된 속성으로 새 프로세스를 만들 수 있습니다.
- ProcessBuilder는 동기화 된 클래스가 아니며, 명시적으로 동기화되지 않으면 멀티 스레드로부터 안전하지 않습니다.
- cmd로 명령을 수행하거나 외부 파일을 실행할 수 있습니다.
- 이번 글에서는 python을 실행하는것을 예제로 글을 작성하겠습니다.
2. ProcessBuilder 사용
Process process = null;
try {
ProcessBuilder pb = new ProcessBuilder(
"python.exe_path",
System.getProperty("user.dir")
+ "\\src\\main\\resources\\python\\FaceValidation.py", fileName);
process = pb.start();
}catch (Exception e) {
throw new RuntimeException("프로세스 실행 에러");
}
ProcessBuilder의 매개변수 하나씩 살펴보겠습니다.
- python.exe_path = "python 인터프리터 실행파일의 위치/python" 즉 실행파일까지 포함하여 작성해야합니다.
폴더까지만 적었다가 개고생.. - System.getProperty("user.dir")+ "\\src\\main\\resources\\python\\FaceValidation.py" : 실행할 python파일의 위치
- fileName: python파일로 넘겨줄 매개변수
이렇게 설정하고 실행합니다.
try {
process.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException("대기 에러");
}
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "euc-kr"));
String line = "";
String output = "";
while((line = reader.readLine()) != null) {
output += line;
output += "\n";
}
if(!output.equals("")){
log.info("output= {}", output);
return FaceValidation.builder()
.valid(true)
.build();
}else{
throw new InvalidImageException();
}
} catch (Exception e) {
throw new InvalidImageException();
}
BufferdReader를 이용하여 Python 실행 결과를 가져올 수 있습니다.
output에 encoding 값을 가져오도록 했는데 얼굴이 인식되지 않는다면 InvalidImageException을 던지게됩니다.
인식된다면 FaceValidation이라는 객체에 true값을 담아 메서드가 종료됩니다.
전체 코드는 다음과 같습니다
public class FaceValidation {
private boolean valid;
public static FaceValidation validation(String fileName){
Process process = null;
try {
ProcessBuilder pb = new ProcessBuilder(
"python.exe_path",
System.getProperty("user.dir")
+ "\\src\\main\\resources\\python\\FaceValidation.py", fileName);
process = pb.start();
}catch (Exception e) {
throw new RuntimeException("프로세스 실행 에러");
}
try {
process.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException("대기 에러");
}
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "euc-kr"));
String line = "";
String output = "";
while((line = reader.readLine()) != null) {
output += line;
output += "\n";
}
if(!output.equals("")){
System.out.println("output="+output);
log.info("output= {}", output);
return FaceValidation.builder()
.valid(true)
.build();
}else{
throw new InvalidImageException();
}
} catch (Exception e) {
throw new InvalidImageException();
}
}
}
3. Python 코드
import cv2
import face_recognition
import sys
model_method = 'cnn'
input_string = sys.argv[1]
# image injection
file_name = input_string
# read image
image = cv2.cvtColor(cv2.imread(file_name), cv2.COLOR_BGR2RGB)
# set face boxes
boxes = face_recognition.face_locations(image, model=model_method)
# get encoding file
encodings = face_recognition.face_encodings(image, boxes)
for encoding in encodings:
print(encoding)
자바의 PeocessBuilder로 넘겨준 매개변수는 sys.argv 배열에 담깁니다
현재 배열에 담겨있는값은 다음과 같습니다
sys.argv[0] : 실행할 python의 경로
sys.argv[1] : fileName
따라서 넘겨준 fileName을받아 python에서도 file_name으로 저장해줍니다.
이후 opencv와 face_recognition을 사용하여 face detection을 진행하고 encoding값을 출력합니다.
4. 결과
위 방법을 통해 sk open api 호출은 확실히 줄일 수 있습니다. 하지만 성능상의 문제가 있었습니다. 제 로컬 스펙으로 위 로직 유무에따른 실행시간 비교가 다음과 같이 차이가 났습니다.
자체적 valid 없는 경우 : 약 0.5초
자체적 valid 있는 경우 : 약 1.9초
어느정도 예상했지만 생각보다 성능이 매우 떨어지는걸 확인하여 위 방법은 프로젝트에 사용하기 어렵다고 생각됩니다.
cnn방식이 아닌 hog방식이나 haarcascading 방식등 좀 더 빠르고 가벼운 방식은 안경이나 모자를 쓴 사진은 인식률이 매우떨어지는 등 인식률에 문제가 있어 api 호출이 야기될것이므로 다른 방식의 유효성 검사가 필요할것 같습니다.
'Java > SpringBoot' 카테고리의 다른 글
[SpringBoot] @Transactional을 알아보자 (0) | 2024.02.01 |
---|---|
[SpringBoot] SSE알림과 비동기 (0) | 2023.12.16 |
스프링 부트 환경변수 설정하기 (0) | 2023.09.19 |