and camera using gray filter
지난 글에서는 안드로이드에서 OpenCV를 사용하는 방법에 대해 알아보았다. 환경 구축과 Edge를 검출하는 예제를 다뤘다. 그때 구현한 환경 및 코드를 그대로 사용하여 추가 구현을 해볼 것이다.
1. 메인 메뉴 화면 구성
2. Gray 카메라 필터
3. 실시간 얼굴 인식
기존 프로젝트에 추가 기능을 구현하는 것이기 때문에 여러 기능을 개별로 확인할 수 있는 메뉴를 제공하도록 한다. 기존 Edge 검출 관련 코드는 다음의 첨부파일을 참고하면 된다.
간단하게 다음과 같이 4개의 버튼으로 구성할 것이다. Edge Detection 버튼을 누르면 이전에 구현했던 Edge 검출기 화면이 그대로 보이면 된다. 나머지는 곧바로 해당 기능을 실행한다. 레이아웃은 매우 간단하니 별도로 코드에 대해서 언급하지 않는다. (첨부파일 참고)
메인 메뉴를 위한 MainActivity를 생성한다. 각 버튼을 눌렀을 때 해당 기능을 동작할 액티비티를 시작하는 간단한 역할을 한다. 액티비티 시작 시 넘겨주는 데이터는 기능 구별을 위해서 필요한 정보다.
이 정도로 메인 메뉴는 쉽게 구성을 끝낸다.
Gray 카메라와 실시간 얼굴 검출은 모두 카메라를 사용하기 때문에 같은 액티비티에서 처리하도록 한다. 카메라에서 받은 Mat 데이터를 기반으로 하기 때문이다.
OpenCV에서 카메라를 사용하는 방법은 안드로이드 Camera API보다 쉽다.
CameraBridgeViewBase.CvCameraViewListener2란 리스너를 implements 하면 다음 3개의 함수를 오버 라이딩해야 한다.
onCameraViewStarted() : 카메라 프리뷰가 시작되면 호출된다.
onCameraViewStopped() : 카메라 프리뷰가 어떤 이유로 멈추면 호출된다.
onCameraFrame() : 프레임 전달이 필요한 경우에 호출된다.
카메라 프리뷰를 보여줄 뷰는 OpenCV에서 제공되는 JavaCameraView라는 클래스를 사용한다. 해당 클래스 객체 mCameraView를 생성하면 다음과 같이 뷰를 보여주도록 설정하고 리스너를 설정하고 enableView()를 하면 된다. setCameraIndex()는 전면/후면 카메라를 설정하는 함수이다. 파라미터가 0이면 후면이고 1이면 전면이다.
mCameraView.setVisibility(SurfaceView.VISIBLE);
mCameraView.setCvCameraViewListener(this);
mCameraView.enableView();
mCameraView.setCameraIndex(0);
enableView()는 다음과 같이 onResume()에서 OpenCV 라이브러리가 로딩되었는지 확인한 후 사용한다. OpenCV가 호출되기 전에 사용하면 당연히 문제가 발생하여 앱이 죽는다.
카메라를 통해서 Mat 데이터인 프레임을 수신한다. rgba()는 RGBA Mat을 리턴하는 함수이다. 컬러 데이터이기 때문에 Gray로 변환해주기 위해서 ConvertRGBtoGray() 함수를 사용한다.
그러나 사실 프레임을 받을 때 rgba()가 아닌 gray() 함수로 받을 수도 있다. 이름 그대로 gray scale Mat을 리턴하는 함수이다. 그러면 별도로 변환할 필요가 없는 것이다. 즉 다음과 같이 구현이 가능하다.
mMode는 메인 메뉴에서 액티비티 실행 시 전달한 정보이다. Gray 카메라 실행 시 2를 넘겨주었다.
얼굴 검출을 하기 위해서는 일단 스마트폰에 haarcascade_frontalface_default.xml 파일을 넣어야 한다.
해당 파일은 OpenCV-android-sdk\sdk\etc\haarcascades 내에 있다.
해당 파일에 대한 경로를 설정해야 하기 때문에 내장 메모리 최상단에 넣어두는 것이 편하다.
실시간 얼굴 검출을 위한 함수는 detectFace()이다. Gray 카메라를 위한 프레임 컨버터 함수를 실행했던 onCameraFrame() 내부에서 호출하면 된다. 왜냐하면 실시간 얼굴 검출을 위해서 카메라에서 받아오는 프레임 정보가 필요하기 때문이다.
얼굴 검출에 있어서 주요 함수는 detectMultiScale()이다. 여기에서 사용된 원형은 다음과 같다.
public void detectMultiScale(Mat image, MatOfRect objects, double scaleFactor, int minNeighbors, int flags, Size minSize)
각 파라미터의 의미는 다음과 같다.
image : 카메라에서 가져온 프레임을 가공하여 검출하고자 하는 대상이 있는 확인하기 위한 원본 이미지
objects : 검출된 객체를 포함한 직사각형의 벡터
scaleFactor : 이미지 피라미드에서 사용되는 scale 계수 값
minNeighbors : 이미지 피라미드에서 슬라이딩 윈도우가 대상을 검출한 횟수가 지정한 값 이상이면 유효
flags : 구 버전 OpenCV에서 필요
minSize : 검출하려는 이미지의 최소 사이즈로 해당 값보다 작으면 무시
scaleFactor와 minNeighbors 파라미터는 (적어도 나에겐) 다소 생소한 개념이다.
두 파라미터를 이해하기 위해서는 영상 처리에서 스케일(scale)이란 개념부터 알아야 한다. 스케일이 크다는 것은 우리가 숲을 볼 때 숲 전체를 보는 것이고 스케일이 작다는 것은 망원경을 통해서 숲의 특정 부분을 확대해서 보는 것이라고 생각하면 된다. 다음 그림을 보면 이해가 쉬울 것이다.
이미지에서 어떤 객체를 검출할 때 기본적으로 여러 가지 스케일에 대해 분석한다. 이때 필요한 여러 스케일을 만들어 내는 기준이 되는 값이 scaleFactor 인자이다. 원본 이미지의 스케일을 크게 만들기 위해서 이미지 크기를 1/(scaleFactor)^n씩 축소한다. 이미지가 작아질수록 한눈에 전부 들어온다. 이 말은 곧 숲 전체를 볼 수 있다는 의미이고 스케일이 크다는 뜻이다.
이런 식으로 여러 스케일의 이미지를 모은 것을 이미지 피라미드(image pyramid)라고 한다. 그러면 고정된 크기의 윈도우를 기준으로 하여 각 스케일의 이미지 내부를 이동하며 전체를 탐색한다. 이때 사용되는 윈도우를 슬라이딩 윈도우(sliding window)라고 한다.
이미지 피라미드에는 여러 스케일 이미지가 있다. 그 스케일 이미지들 중에서 슬라이딩 윈도우가 이동하며 찾고자 하는 대상을 모든 이미지에서 찾을 수는 없을 것이다.
위 그림을 보자. 슬라이딩 윈도우의 꽃 이미지와 유사한 것을 찾기 위해 윈도우는 이미지 내부에서 이동할 것이다. 가장 좌측 이미지는 스케일이 작기 때문에 디테일하게 꽃 이미지만 특정하기 어렵다. 우측으로 이동할수록, 즉 스케일이 커질수록 검출 가능성이 높아진다.
이렇게 여러 스케일 이미지에서 최소 몇 번이나 검출이 되어야 실제로 유효한 결과라고 판단할지 정하는 값이 minNeighbors이다. 예를 들어 minNeighbors가 3이라면 여러 스케일의 이미지 중에서 최소 3번 검출되어야 찾고자 하던 대상이 맞다고 판단하는 것이다.
첨부파일 참고해서 코드를 완성하여 실행하면 다음과 같이 잘 동작한다. 얼굴이 아닌 부분도 얼굴로 인식을 하고 사람이 많으면 전부 인식하지 못하는 등의 문제점은 있었다.
확실히 OpenCV 얼굴 검출기에 비해서 예전에 테스트했던 구글 FaceTracker가 인식률이 좋았다. 또한 OpenCV 얼굴 검출 사용 시 카메라 프레임도 상당히 떨어지는걸 보아 연산이 무거운 것 같다.
ref.)
얼굴 검출 : https://kkokkal.tistory.com/1329
detectMultiScale() : https://dreamaz.tistory.com/418
detectMultiScale() parameter : https://docs.opencv.org/3.4/d1/de5/classcv_1_1CascadeClassifier.html#ab3e572643114c43b21074df48c565a27
detectMultiScale() minNeighbors : https://stackoverflow.com/questions/22249579/opencv-detectmultiscale-minneighbors-parameter
이미지 피라미드 : https://darkpgmr.tistory.com/137
스케일 스페이스 : https://bskyvision.com/144