Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
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
Tags
more
Archives
Today
Total
관리 메뉴

임도현의 성장

OpenCV 얼굴인식(Face Detection) 사용해보기 본문

Spring Boot

OpenCV 얼굴인식(Face Detection) 사용해보기

림도현 2025. 4. 3. 22:24

📻OpenCV 란?

Open Source Computer Vision의 약자로 영상 처리에 사용할 수 있는 오픈 소스 라이브러리입니다. OpenCV는 이미지와 비디오 처리, 물체 추적, 얼굴 인식, 패턴 인식 등 다양한 컴퓨터 비전 기술을 구현하는 데 필요한 강력한 도구들을 제공합니다. OpenCV는 C++로 개발되었으며, Python, Java, 그리고 여러 다른 언어 바인딩을 통해 다양한 플랫폼에서 사용할 수 있습니다.

😰 컴퓨터 비전이란?

컴퓨터 비전(Computer Vision, CV)은 인간의 시각적 능력을 모방하여 이미지를 분석하고 해석하며, 이를 바탕으로 "의사 결정을 내리는 인공지능(AI) 기술입니다." 컴퓨터 비전을 활용하면 이미지, 패턴, 객체를 감지하고 인식할 수 있습니다. 이 기술은 자율주행차, 의료 영상 분석, 온라인 쇼핑 등 다양한 분야에서 핵심적인 역할을 수행하고 있습니다.

📚얼굴 인식을 어떻게 할까?

OpenCV가 이미지만 보고 얼굴을 인식하는 원리는 "머신 러닝"과 "패턴 인식" 기술을 기반으로 합니다. 가장 일반적으로 사용되는 얼굴 인식 방법은 Haar Cascade Classifier입니다.

🍪Haar Cascade Classifier란?

Haar Cascade Classifier는 Haar 특징(Haar Features)와 기계 학습 알고리즘을 활용한 얼굴 검출 알고리즘입니다.

  • Haar 특징(Haar Features) : Haar 특징은 기본적으로 이미지 내에서 특정 부분의 명도 차이를 나타내는 특징입니다. 이 특징들은 주로 사각형 형태로 나누어지고, 각 사각형의 밝기 차이를 비교하여 이미지의 패턴을 인식합니다.
  • Cascade Classifier : 이미지에서 큰 특징을 찾고, 이후 단계에서 점점 더 작은 특징을 찾아가며 얼굴을 정확히 인식합니다.

Haar Cascade Classifier는모델 이 수많은 얼굴을 학습하여 빠르게 얼굴의 특징, 눈, 코, 입의 배치 및 밝기 차이 등을 찾아 얼굴을 인식 할 수 있는것입니다.

👨‍💻IntelliJ 라이브러리 등록

build.gradle에 OpenCV를 버전에 맞게 추가해주고 OpenCV는 native library로 dll 파일을 연결해주어야 합니다. 저 같은 경우에는 ddl 파일을 libs/native/ 경로에 저장을 하고 VM Option에 네이티브 라이브러리 경로 설정을 하였습니다.

implementation 'org.openpnp:opencv:4.5.5-1'

🌐OpenCV 샘플 확인

https://github.com/opencv/opencv/blob/4.x/samples/java/eclipse/HelloCV/src/Main.java

OpenCV 설치를 하고 깃허브에서 샘플을 찾아 실행 보면 응답 값을 확인 할 수 있다. native library로 dll 파일을 연결해두지 않았으면 loadLibrary가 파일을 찾지 못해 에러가 난다.

public class Main {
    public static void main(String[] args) {
        System.out.println("Welcome to OpenCV " + Core.VERSION);
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat m  = Mat.eye(3, 3, CvType.CV_8UC1);
        System.out.println("m = " + m.dump());
    }
}

결과 값 :
Welcome to OpenCV 4.5.5
m = [  1,   0,   0;
   0,   1,   0;
   0,   0,   1]

🚨예제 코드

face detection을 java로 사용하는 코드는 아래 사이트를 참고 하였다. 

https://www.tutorialspoint.com/opencv/opencv_face_detection_in_picture.htm

💫Controller Layer

클라이언트가 file이라는 이름으로 보낸 이미지를 MultipartFile 객체로 받아 faceDetectionService. detectFaces() 메서드 함수에 전달하여 OpenCV를 활용하여 얼굴을 찾아서 처리된 이미지를 반환 받아 응답하는 데이터가 PNG이미지임을 명시합니다.

@PostMapping("/face")
public ResponseEntity<byte[]> facesDetect(@RequestParam("file") MultipartFile file) {
    try {
        byte[] processedImage = faceDetectionService.detectFaces(file);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);

        return new ResponseEntity<>(processedImage, headers, HttpStatus.OK);
    } catch (IOException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
    }
}

📜Service Layer

이미지를 디스크에 저정하지 않고 메모리에서 직접 처리 할 수 있도록 하기 위해 byte[] 형식으로 변환 즉, 파일을 저장하는 추가적인 과정 없이 바로 클라이언트에 응답 할 수 있도록 하기 위함입니다.

public byte[] detectFaces(MultipartFile file) throws IOException {
    Mat image = Imgcodecs.imdecode(new MatOfByte(file.getBytes()), Imgcodecs.IMREAD_COLOR);

    Mat processedImage = faceDetectionProcessor.detectFaces(image);

    MatOfByte buffer = new MatOfByte();
    Imgcodecs.imencode(".png", processedImage, buffer);
    return buffer.toArray();
}
  1. file.getBytes() : 이미지 파일을 byte[]로 변환 
  2. new MatOfByte(file.getBytes()) : OpenCV에서 사용할 수 있도록 byte[] 을 MatOfByte 객체로 래핑
  3. Imgcodecs.imdecode() : 바이트 데이터를 OpenCV Mat 객체로 변환 Imgcodecs.IMREAD_COLOR: 컬러 이미지로 로드
Mat image = Imgcodecs.imdecode(new MatOfByte(file.getBytes()), Imgcodecs.IMREAD_COLOR);
Mat processedImage = faceDetectionProcessor.detectFaces(image);
  • MatOfByte buffer : 이미지를 바이트 배열로 변환하기 위한 OpenCV 객체
  • Imgcodecs.imencode(".png", processedImage, buffer) : Mat 객체를 PNG 파일 형식으로 변환 변환된 데이터는 buffer에 저장
  • buffer.toArray() : MatOfByte 객체를 byte[] 형태로 변환하여 반환 즉, 얼굴을 검출한 후 처리된 이미지를 PNG 형식의 바이트 배열로 반환하는 역할
MatOfByte buffer = new MatOfByte();
Imgcodecs.imencode(".png", processedImage, buffer);
return buffer.toArray();

🛠OpenCVUtil Layer

init()은 native library가 잘 등록 되어있는지 확인 하는 메서드입니다.

loadCascade()는 LBP Cascade XML파일을 로드 하는 메서드입니다다. cascadePath는 Haar Cascade XML 파일 또는 LBP Cascade XML 파일을 저장하여 파일 경로를 읽도록합니다. 저는 깃허브에서 LBP 형식을 사용하였습니다.

https://github.com/opencv-java/face-detection/blob/master/resources/lbpcascades/lbpcascade_frontalface.xml

@Slf4j
@Component
public class OpenCVUtil implements FaceDetectionProcessor{

    private CascadeClassifier faceCascade;

    @PostConstruct
    public void init() {
        try {
            log.info("=================== OpenCV 라이브러리 로드 시작 ===================");
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
            log.info("=================== OpenCV 라이브러리 로드 성공 = {} ============", Core.VERSION);
        } catch (UnsatisfiedLinkError e) {
            throw new RuntimeException("OpenCV 라이브러리를 로드할 수 없습니다. 환경 변수를 확인하세요.", e);
        }
    }

    @PostConstruct
    public void loadCascade() {
        try {
            String cascadePath = "C:/Class/MyOpencv/src/main/java/com/opencv/global/LbpCascade.xml";
            faceCascade = new CascadeClassifier(cascadePath);

            if (faceCascade.empty()) {
                throw new IOException("LBP Cascade 파일을 로드할 수 없습니다.");
            }
        } catch (IOException e) {
            throw new RuntimeException("Error loading LBP Cascade file", e);
        }
    }

    @Override
    public Mat detectFaces(Mat image) {
        Mat grayImage = new Mat();
        Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);

        MatOfRect faces = new MatOfRect();
        faceCascade.detectMultiScale(grayImage, faces, 1.1, 3, 0, new Size(30, 30), new Size());

        log.info("찾은 얼굴 수 = {}",faces.toArray().length);

        for (Rect face : faces.toArray()) {
            Imgproc.rectangle(image, face.tl(), face.br(), new Scalar(0, 0, 255), 2);
        }

        return image;
    }
}

📈detectFaces 메서드 상세 설명

  • Mat grayImage : OpenCV에서는 얼굴 검출을 할 때 흑백 이미지가 성능이 더 좋기 때문에 그레이스케일 변환된 이미지를 저장할 Mat객체 생성
  • Imgproc.cvtColor() : 컬러 이미지를 그레이스케일(흑백)로 변환하는 OpenCV 메서드입니다. 
  • MatOfRect faces : 검출된 얼굴 영역들을 저장하는 OpenCV 객체, 여러 개의 얼굴을 검출할 수 있으므로 배열 형태로 저장
  • faceCascade.detectMultiScale() : 얼굴을 검출하는 OpenCV의 주요 메서드입니다.  faceCascade는 CascadeClassifier 객체로, LBP 또는 Haar Cascade 알고리즘을 사용해 얼굴을 찾습니다.
    1. grayImage: 그레이스케일 변환된 이미지를 사용 (컬러보다 정확도가 높음)
    2. faces: 얼굴이 감지된 위치를 저장할 객체 (MatOfRect)
    3. 1.1: 이미지 스케일 팩터 (1.1배씩 이미지 크기를 줄여가면서 얼굴을 찾음)
    4. 3: 최소 이웃 수 (이 값이 클수록 더 정확한 얼굴만 검출, 작은 값이면 오탐 가능성 증가)
    5. 0: 추가적인 옵션 플래그 (일반적으로 0을 사용)
    6. new Size(30, 30): 최소 얼굴 크기 (너무 작은 얼굴은 무시함, 적절하게 조절)
    7. new Size(): 최대 얼굴 크기 (비워두면 제한 없음)
  • for (Rect face : faces.toArray()) : 감지된 얼굴들의 리스트를 배열로 변환 한 후 Rect face에 사각형 좌표 정보를 저장 (x, y, width, height) 형식으로 얼굴의 위치를 나타냄]
  • Imgproc.rectangle() : 검출된 얼굴 위치에 사각형을 그리는 메서드
    1. image: 얼굴을 감지한 원본 이미지 (사각형을 이 이미지에 그림)
    2. face.tl(): 사각형의 좌상단(top-left) 좌표
    3. face.br(): 사각형의 우하단(bottom-right) 좌표
    4. new Scalar(0, 0, 255): 빨간색(RED) 사각형 (BGR 형식으로 0,0,255는 빨간색)
    5. 2: 사각형의 두께(픽셀 단위)

🥳얼굴 인식 과정

  1. 이미지 전처리 (그레이스케일 변환)
  2. LBP Cascade 분류기 로딩
  3. 얼굴 검출 (DetectMultiScale)
  4. 검출된 얼굴 영역 표시

🏆실행 결과

프로그램을 실행시키면 주어진 이미지 파일에서 얼굴을 자동으로 인식하여 인식된 얼굴 위치에 빨간색 사각형을 그린 후, 결과 이미지를 저장하는 라이브러리를 사용해 보았습니다. 이제 이 방식을 기반으로 다양한 프로젝트에 활용할 수 있을거 같습니다. 

얼굴 인식 전
얼굴 인식 후