아주 단순하게, 가우시안 필터와 캐니 엣지 검출에 대해서만 OpenCV 3.2 vs. OpenCV 3.3.0-rc 두 버전 간의 성능을 비교해봤습니다.
소스 코드는 매우 단순하게 레나 영상을 불러와서 가우시안 블러 & 캐니 엣지 검출을 100번 수행하도록 하였고, 그 실행 시간을 측정하여 출력했습니다.
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main(void)
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
cout << "Running GaussianBlur & Canny on 512x512 lenna image (100 times): " << endl;
int64 t1 = getTickCount();
Mat blurred, dst;
for (int i = 0; i < 100; i++) {
GaussianBlur(src, blurred, Size(), 1.0, 1.0);
Canny(blurred, dst, 50, 150);
cout << ".";
}
int64 t2 = getTickCount();
cout << "\nIt took " << (t2 - t1) * 1000 / getTickFrequency() << " ms." << endl;
return 0;
}
그 결과 OpenCV 3.2 버전을 사용하여 빌드했을 때에는 결과가 아래와 같구요..
OpenCV 3.3.0-rc 버전을 사용하여 빌드했을 때에는 결과가 이렇습니다.
미세하지만 3.3.0-rc 버전이 더 빠르게 동작하는 것 같습니다. OpenCV 3.3.0-rc 버전은 220~230ms 정도 걸리는데, OpenCV 3.2 버전은 240~260ms 정도로 분포하고 있네요. 좀 더 많은 함수를 사용해서 테스트를 해보면 좋겠지만, 일단은 이 정도만..
작년 4월인가에 라즈베리파이3B 모델을 사고, 라즈베리파이 전용 카메라 모듈도 구입을 했습니다. 이때만해도 8M 카메라 모듈이 나오기 전이라 5M 모듈만 있을 때였죠. 이거 사자마자 거의 같은 가격으로 8M 모듈이 발표되서 슬펐던 기억이 있는데.. 암튼 그때만해도 라즈베리파이에 카메라를 붙이고 OpenCV를 깔아서 뭐라도 해볼 요량이었지만, 어쩌다 보니 카메라를 사놓고 별것 해보지도 못하고 구석에 쳐박아 두었었네요.
이번에 다시 라즈비안 OS를 새로 깔고 새마음으로 라즈베리파이에 카메라를 붙였습니다. OpenCV는 이미 깔아놨고, 이번에는 라즈베리파이 카메라를 사용해보려고 합니다. 라즈베리파이카메라를 사용하기 위해 raspicam라는 이름의 라이브러리를 사용할 것입니다. raspicam 라이브러리에 대한 설명은 아래 링크를 참고합니다.
위 사이트에 쓰여있듯이 raspicam은 BSD 라이센스를 따르기 때문에 자유롭게 이용이 가능하고, 일반 C++ 인터페이스뿐만 아니라 OpenCV 인터페이스도 지원합니다. raspicam 라이브러리도 소스 코드를 다운받아서 직접 빌드를 진행해야 라이브러리 파일이 생성됩니다. 설치와 관련되어서는 아래 블로그 설명이 잘 되어 있네요.
위와 같은 cmake 설정을 하고나면 화면에 출력되는 메시지가 cmake_messages.txt 파일에도 함께 저장됩니다. 이때 CREATE OPENCV MODULE=1 이라는 메시지가 출력이 되어야 raspicam 라이브러리가 OpenCV를 인식하고, OpenCV에서 함께 사용할 수 있도록 라이브러리를 생성합니다. cmake_messages.txt 파일의 전체 내용을 아래 [더보기]에 나타내었습니다.
-- The C compiler identification is GNU 4.9.2 -- The CXX compiler identification is GNU 4.9.2 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Found OpenCV: /usr/local (found version "3.2.0") -- ------------------------------------------------------------------------------- -- GNU COMPILER -- ------------------------------------------------------------------------------- -- Adding cv library -- -- ------------------------------------------------------------------------------- -- General configuration for raspicam 0.1.6 -- ------------------------------------------------------------------------------- -- Built as dynamic libs?:ON Compiler:/usr/bin/c++ -- C++ flags (Release): -std=c++0x -Wl,--no-as-needed -Wall -ffunction-sections -fomit-frame-pointer -O2 -ffast-math -DNDEBUG -lpthread -- C++ flags (Debug): -std=c++0x -Wl,--no-as-needed -Wall -ffunction-sections -g3 -O0 -DDEBUG -D_DEBUG -W -Wextra -Wno-return-type -lpthread -- CMAKE_CXX_FLAGS: -std=c++0x -Wl,--no-as-needed -Wall -ffunction-sections -- CMAKE_BINARY_DIR: /home/pi/raspicam-0.1.6/build -- -- CMAKE_SYSTEM_PROCESSOR = armv7l -- BUILD_SHARED_LIBS = ON -- BUILD_UTILS = ON -- CMAKE_INSTALL_PREFIX = /usr/local -- CMAKE_BUILD_TYPE = Release -- CMAKE_MODULE_PATH = /usr/local/lib/cmake/;/usr/lib/cmake -- -- CREATE OPENCV MODULE=1 -- CMAKE_INSTALL_PREFIX=/usr/local -- REQUIRED_LIBRARIES=/opt/vc/lib/libmmal_core.so;/opt/vc/lib/libmmal_util.so;/opt/vc/lib/libmmal.so -- -- -- Change a value with: cmake -D<Variable>=<Value> -- -- Configuring done -- Generating done -- Build files have been written to: /home/pi/raspicam-0.1.6/build
cmake가 정상적으로 동작하였으면 이제 실제 빌드 작업을 수행합니다. 기본적으로 make -j2 명령을 입력하면 되고, 혹시나 하는 마음에 빌드 메시지도 함께 기록합니다. 빌드가 완료되면 sudo make install 명령으로 실제 라이브러리를 시스템 폴더에 복사합니다.
pi@GoPiGo:~/raspicam-0.1.6/build $make -j2 2>&1 | tee build_messages.txt pi@GoPiGo:~/raspicam-0.1.6/build $sudo make install pi@GoPiGo:~/raspicam-0.1.6/build $sudo ldconfig
위 사진은 sudo make install 명령을 입력했을 때의 모습입니다. 일단 raspicam 라이브러리 설치가 완료되었습니다. 참 쉽죠? :-)
그러면 이제 raspicam 라이브러리와 OpecCV 라이브러리를 이용하여 카메라 입력을 받고 간단한 얼굴 검출 프로그램을 만들어보겠습니다. 홈디렉토리 아래에 coding/opencv/facedetect 디렉토리를 만들고, 여기에 main.cpp 파일을 생성합니다. main.cpp 파일은 아래와 같이 작성합니다.
#include <opencv2/opencv.hpp>
#include <iostream>
#include <raspicam/raspicam_cv.h>
using namespace cv;
using namespace std;
int main(int argc, char* argv[]) {
raspicam::RaspiCam_Cv cam;
cam.set(CV_CAP_PROP_FORMAT, CV_8UC3);
cam.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cam.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
if (!cam.open()) {
cerr << "Camera open failed!" << endl;
return -1;
}
CascadeClassifier cascade("haarcascade_frontalface_default.xml");
if (cascade.empty()) {
cerr << "Failed to open xml file!" << endl;
return -1;
}
Mat frame, gray, reduced;
int64 t1, t2;
bool do_flip = false;
while (1) {
cam.grab();
cam.retrieve(frame);
if (do_flip)
flip(frame, frame, -1);
t1 = getTickCount();
cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
resize(gray, reduced, Size(0, 0), 0.5, 0.5);
vector<Rect> faces;
cascade.detectMultiScale(reduced, faces, 1.1, 3, 0, Size(40, 40));
for (size_t i = 0; i < faces.size(); i++) {
Rect rc;
rc.x = faces[i].x << 1;
rc.y = faces[i].y << 1;
rc.width = faces[i].width << 1;
rc.height = faces[i].height << 1;
rectangle(frame, rc, Scalar(0, 0, 255), 3);
}
t2 = getTickCount();
cout << "It took " << (t2 - t1) * 1000 / getTickFrequency() << " ms." << endl;
imshow("frame", frame);
int k = waitKey(1);
if (k == 27)
break;
else if (k == 'f' || k == 'F')
do_flip = !do_flip;
}
cam.release();
destroyAllWindows();
}
소스 코드가 그리 길지는 않죠? 일단 raspicam::RaspiCam_Cv 객체를 생성해서 카메라를 열고, 매 프레임마다 Haar Cascade Classifier로 얼굴을 검출합니다. 속도 향상을 위해서 매 프레임을 가로, 세로 1/2 크기로 줄인 다음에 얼굴을 검출하고, 검출된 얼굴 영역을 화면에 보여줄 때 다시 좌표를 2배 확대합니다. 제가 라즈베리파이에 카메라를 거꾸로 달아놔서 'f' 키를 누르면 영상을 flip 하여 얼굴 검출을 수행합니다.
이제 터미널 창에서 make 명령을 입력하면 프로그램을 빌드하여 facedetect라는 실행 파일을 생성합니다.
위에서 haarcascade_frontalface_default.xml 파일은 OpenCV를 설치할 때 /usr/local/share/OpenCV/haarcascades 디렉토리에 만들어집니다. 그러므로 cp /usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml . 명령으로 현재 폴더로 복사하면 됩니다.
이제 터미널 창에서 ./facedetect 명령으로 프로그램을 실행하면 아래처럼 얼굴을 검출합니다.
왼쪽 상단에 나타난 창은 htop 프로그램인데요, 4개의 코어가 다 동작하는 것을 확인할 수가 있구요... 한 프레임에서 얼굴을 찾는데 대략 130~200 ms 정도의 시간이 걸리는 것을 확인할 수 있습니다.
약간의 속도 향상을 위해서 소스 코드에서 cascade.detectMultiScale(reduced, faces, 1.1, 3, 0, Size(40, 40)); 부분을 cascade.detectMultiScale(reduced, faces, 1.2, 3, 0, Size(60, 60), Size(240, 240)); 형태로 수정하고 프로그램을 다시 돌려보니 조금 빨라졌네요. :-)
소리 소문도 없이 OpenCV 3.3.0-rc 버전이 릴리즈되었습니다. OpenCV github releases 페이지를 보니 3일 전에 OpenCV 3.3.0-rc 버전이 소스 파일 형태로 업데이트가 되었다고 표시되어 있는데요, opencv_contrib 쪽 소스 코드의 3.3.0-rc 버전은 10일전에 올라왔다고 되어 있습니다. 아마도 2017년 6월 30일 경에 3.3.0-rc 버전이 모두 릴리즈되고 메인 소스 쪽에만 약간의 업데이트가 있었던 것은 아닌가 싶습니다. OpenCV 3.3.0-rc 버전은 아래 링크에서 다운로드 받을 수 있습니다.
OpenCV 3.3.0-rc 버전 릴리즈 소식은 OpenCV 공식 사이트에서도 언급이 되어 있지 않습니다. 조용히 RC 버전을 릴리즈하고 내부적으로 테스트를 하고 있는 것이 아닌가 싶은데요, 어쨋든 새롭게 릴리즈된 버전이고 하니 다운로드 받아서 직접 빌드하는 방법을 알아보겠습니다.
기존에 OpenCV 3.2 버전을 빌드하는 방법은 아래 블로그와 유투브에서 설명한 적이 있습니다. 혹시 OpenCV 빌드를 한 번도 안해보신 분들은 아래 글과 동영상을 먼저 훑어보시는 것을 권장하구요..
개인적으로 OpenCV 빌드 작업은 C:\opencv 폴더를 만들어서 진행을 합니다. 그러므로 C:\opencv 폴더를 만들고, 여기에 OpenCV 3.3.0-rc 소스 코드의 압축 파일을 해제합니다. opencv 메인 소스와 opencv_contrib 소스 코드를 모두 압축 해제하면 아래와 같은 폴더 구조를 가지게 됩니다.
위 그림에서 mybuild 폴더는 실제 빌드 작업을 진행할 폴더이며, 여러분이 직접 만들어 주어야 합니다. 일단 폴더 구조를 위와 같이 구성하였으면 이제 CMake 프로그램을 실행하여 Visual Studio 2017 용 솔루션 파일과 프로젝트 파일을 생성합니다. CMake 사용법에 대한 자세한 설명은 위에 링크해놓은 블로그와 동영상을 참고하시기 바라며, 다만 여기서는 CMake 기본 설정에서 변경한 내역만 정리하겠습니다.
OpenCV 3.2에서는 opencv_contrib 쪽 소스 코드를 함께 빌드할 때 BUILD_opencv_world 옵션을 선택하면 에러가 발생하는 버그가 있었는데요, 그런 버그는 이미 예전에 해결이 되었습니다. 그리고 2017년 1월에 발생한 OpenCV 커밋 중에 opencv_contrib 모듈도 opencv_world 쪽으로 포함시키는 내용이 있어서 모든 opencv 기능을 opencv_world330.dll 하나로 사용할 수 있습니다. WITH_CUDA 선택을 해제한 이유는 CUDA가 Visual Studio 2017을 지원하지 않기 때문입니다.
found Intel IPP (ICV version): 2017.0.2 [2017.0.2] at: C:/opencv/mybuild/3rdparty/ippicv/ippicv_win found Intel IPP IW binaries: 2017.0.2 at: C:/opencv/mybuild/3rdparty/ippicv/ippicv_win/../ippiw_win/ Could not find OpenBLAS include. Turning OpenBLAS_FOUND off Could not find OpenBLAS lib. Turning OpenBLAS_FOUND off A library with BLAS API not found. Please specify library location. LAPACK requires BLAS A library with LAPACK API not found. Please specify library location. Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE) Could NOT find JNI (missing: JAVA_AWT_LIBRARY JAVA_JVM_LIBRARY JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH) Could NOT find Matlab (missing: MATLAB_MEX_SCRIPT MATLAB_INCLUDE_DIRS MATLAB_ROOT_DIR MATLAB_LIBRARIES MATLAB_LIBRARY_DIRS MATLAB_MEXEXT MATLAB_ARCH MATLAB_BIN) VTK is not found. Please set -DVTK_DIR in CMake to VTK build directory, or to VTK install subdirectory with VTKConfig.cmake file Caffe: NO Protobuf: NO Glog: NO Looking for tiny_dnn.h Looking for tiny_dnn.h - found Found tiny-dnn in: C:/opencv/mybuild/3rdparty/tinydnn/tiny-dnn-1.0.0a3 The protocol buffer compiler is not found (PROTOBUF_PROTOC_EXECUTABLE='Protobuf_PROTOC_EXECUTABLE-NOTFOUND') freetype2: NO harfbuzz: NO No preference for use of exported gflags CMake configuration set, and no hints for include/library directories provided. Defaulting to preferring an installed/exported gflags CMake configuration if available. Failed to find installed gflags CMake configuration, searching for gflags build directories exported with CMake. Failed to find gflags - Failed to find an installed/exported CMake configuration for gflags, will perform search for installed gflags components. Failed to find gflags - Could not find gflags include directory, set GFLAGS_INCLUDE_DIR to directory containing gflags/gflags.h Failed to find glog - Could not find glog include directory, set GLOG_INCLUDE_DIR to directory containing glog/logging.h Module opencv_sfm disabled because the following dependencies are not found: Eigen Glog/Gflags Processing WORLD modules... module opencv_core... module opencv_flann... module opencv_hdf... module opencv_imgproc... module opencv_ml... module opencv_objdetect... module opencv_phase_unwrapping... module opencv_photo... module opencv_plot... module opencv_reg... module opencv_surface_matching... module opencv_video... module opencv_xphoto... module opencv_bgsegm... module opencv_dnn... Torch importer has been enabled. To run the tests you have to install Torch ('th' executable should be available) and generate testdata using opencv_extra/testdata/dnn/generate_torch_models.py script. module opencv_face... module opencv_fuzzy... module opencv_imgcodecs... module opencv_shape... module opencv_videoio... module opencv_xobjdetect... module opencv_highgui... module opencv_superres... module opencv_bioinspired... module opencv_dpm... module opencv_features2d... module opencv_line_descriptor... module opencv_saliency... module opencv_text... Tesseract: NO module opencv_calib3d... module opencv_ccalib... module opencv_datasets... module opencv_rgbd... module opencv_stereo... module opencv_structured_light... module opencv_tracking... module opencv_videostab... module opencv_xfeatures2d... module opencv_ximgproc... module opencv_aruco... module opencv_optflow... module opencv_stitching... Processing WORLD modules... DONE Processing module opencv_img_hash... Processing module opencv_python3... Processing module opencv_ts...
General configuration for OpenCV 3.3.0-rc ===================================== Version control: unknown
Extra modules: Location (extra): C:/opencv/opencv_contrib-3.3.0-rc/modules Version control (extra): unknown
Video I/O: Video for Windows: YES DC1394 1.x: NO DC1394 2.x: NO FFMPEG: YES (prebuilt binaries) avcodec: YES (ver ) avformat: YES (ver ) avutil: YES (ver ) swscale: YES (ver ) avresample: YES (ver ) GStreamer: NO OpenNI: NO OpenNI PrimeSensor Modules: NO OpenNI2: NO PvAPI: NO GigEVisionSDK: NO DirectShow: YES Media Foundation: NO XIMEA: NO Intel PerC: NO
Parallel framework: Concurrency
Trace: YES (with Intel ITT)
Other third-party libraries: Use Intel IPP: 2017.0.2 [2017.0.2] at: C:/opencv/mybuild/3rdparty/ippicv/ippicv_win Use Intel IPP IW: prebuilt binaries (2017.0.2) Use Intel IPP Async: NO Use Lapack: NO Use Eigen: NO Use Cuda: NO Use OpenCL: YES Use OpenVX: NO Use custom HAL: NO
OpenCL: <Dynamic loading of OpenCL library> Include path: C:/opencv/opencv-3.3.0-rc/3rdparty/include/opencl/1.2 Use AMDFFT: NO Use AMDBLAS: NO
cvconfig.h is in: C:/opencv/mybuild -----------------------------------------------------------------
Configuring done
위와 같이 설정하여 OpenCV.sln 파일을 생성합니다. 그리고 Visual Studio 2017에서 OpenCV.sln 파일을 불러와서 빌드를 진행하면 되는데요... 에러가 하나 발생하네요 ㅠㅠ
C:\opencv\opencv_contrib-3.3.0-rc\modules\ximgproc\src\structured_edge_detection.cpp 파일 584라인과 585 라인에서 M_PI를 사용하는 부분에서 에러가 발생하는데요, 아마도 math.h 파일의 M_PI를 제대로 포함하지 않았기 때문인 것 같습니다. 굳이 opencv 소스 코드에서 M_PI를 사용하는 것보다는 CV_PI를 사용하는 것이 더 깔끔할테니까 아래 그림과 같이 M_PI 대신 CV_PI로 소스 코드를 수정합니다.
그리고는 다시 빌드를 하면 잘 진행이 됩니다. (참 쉽죠? :-)
전체 솔루션 빌드를 진행하고, INSTALL 프로젝트도 따로 빌드를 하고나면 C:\opencv\mybuild\install 폴더에 헤더 파일, LIB 파일, DLL 파일이 사이좋게 모이게 됩니다.
opencv_world330.dll 파일 하나의 크기가 거의 100MByte입니다. 용량이 좀 부담스러울 수도 있지만 OpenCV 공부를 하는 과정에서는 opencv_world330.dll 파일 하나만 있으면 편해서 저는 opencv_world 속성을 선택하는 것을 선호합니다. 회사에서 프로젝트를 진행하거나 배포를 위한 목적이라면 개별 모듈을 따로 빌드하는 것이 더 좋겠지요.
참고로 OpenCV 3.3.0-rc 버전에서는 예전에 opencv_contrib 쪽 소스 구조에 있던 dnn(deep neural network) 모듈이 opencv 메인 소스 쪽으로 이동을 했습니다. 그 외에도 C++11 지원이 추가된 것으로 보이고, CPU 아키텍쳐(SSE, AVX, SIMD, etc)에 대한 소스 코드 분기가 많이 추가된 것 같네요. 공식적인 change log가 없어서 어떤 것이 더 바뀐 것인지는, 나중에 OpenCV 3.3.0 정식 버전이 릴리즈되면 알 수 있을 것 같습니다.