$:~ $ python3 Python 3.4.2 (default, Oct 19 2014, 13:31:11) [GCC 4.9.1] on linux Type "help", "copyright", "credits" or "license"for more information. >>> import numpy as np >>> np.__version__ '1.10.4' >>>
위 스크립은 OpenCV 3.1.0의 CMake 빌드 버그로, OpenCV 3.0.0 까지 -D INSTALL_C_EXAMPLES=ON으로 빌드했지만 3.1.0에서는 -D INSTALL_C_EXAMPLES=OFF로 해야한다. 또한 opencv_contrib 모듈을 함께 빌드한다면 -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules를 함께 사용한다.
컴파일과 설치
그리고 컴파일 한다.
1 2 3 4 5 6 7 8
~(cv)$ make -j4 # RPi2
... [100%] Built target example_ocl_tvl1_optical_flow
real 173m22.432s user 165m58.970s sys 5m7.060s
싱글로 make시 Raspberry Pi 2 에서 빌드시 약 3시간 소요. -j 옵션을 이용시 45분 이상 절약.
$ ls lib/cv2.so $ ls lib/python3/cv2.cpython-34m.so
이 두 라이브러리가 생성되 있으면 빌드가 제대로 된 것이다.
이제 로컬 시스템에 설치를 한다. 보통 Python2.7과 Python3.4가 함께 설치되 상태일 수 있는데 이 경우 시스템에 기본 Python2.7 환경을 기반으로 설치가 된다.
1 2 3 4 5 6 7 8 9 10
(cv)$ sudo make install ... -- Installing: /usr/local/include/opencv2/contrib/detection_based_tracker.hpp -- Installing: /usr/local/lib/python2.7/dist-packages/cv2.so -- Set runtime path of "/usr/local/lib/python2.7/dist-packages/cv2.so" to "/usr/local/lib" -- Installing: /usr/local/lib/python2.7/dist-packages/cv.py -- Installing: /usr/local/lib/libopencv_stitching.so.2.4.11 ...
(cv)$ sudo ldconfig
설치된 opencv 패키지 버전을 확인한다.
1 2
$ pkg-config --modversion opencv 3.1.0
cv2.so
Python2.7이 함께 설치되어 있는 경우 빌드시 Python3.x의 site-packages에 파일 cv2.cpython-34mu.so 로 설치되어 있다. 이 파일은 실제 파이썬 바인딩으로 실제 파이썬 환경에서 이 라이브러리가 필요하기 하다. Python3 가상환경에서 이 파일 심볼릭 링크를 cv 환경에 cv2.so라는 이름으로 연결해 준다.
파이썬은 관행적으로 opencv 라이브러리를 cv2.so 파일로 표현하고 있다. python3의 경우 라이브러리 이름을 cv2로 링크해 준다.
1 2 3 4
$ cd /usr/local/lib/python3.4/dist-packages $ ls cv2.cpython-34m.so numpy numpy-1.11.2.egg-info $ sudo ln -s cv2.cpython-34m.so cv2.so
g:/Root/FFmpegBasic/jni 폴더에 Application.mk 파일을 만듭니다. 내용은 간단히 아래와 같이 한 줄만 작성합니다.
APP_ABI := armeabi-v7a
참고: arm architecture ARMv7-A 이상을 타겟으로 컴파일 하겠다는 옵션입니다. arm CoretexA8 이상의 core가 이에 해당됩니다. 앞서 말씀드린대로 arm11 코어를 사용한 Optimus One, Galaxy Neo 같은 폰에서는 안 돌아가겠지요.
Android.mk 는 폴더마다 여러개를 작성해야 합니다.공통으로 사용할 common.mk 파일을 먼저 작성한 후, 각각 폴더마다 설명하겠습니다.
common.mk
g:/Root/FFmpegBasic/jni/ffmpeg 폴더에 common.mk 파일을 만듭니다.모든 Android.mk에서 공통으로 include 해서 사용할 파일입니다.
common.mk에서는 크게 두가지 일을 할 것입니다.1) 공통으로 사용할 컴파일 옵션을 정의합니다.2) configure를 통해 생성된 파일에서 컴파일 할 소스 파일 이름들을 읽어 저장합니다.
컴파일 옵션은 다음과 같이 한 줄이면 됩니다.
1.COMMON_CFLAGS := -DHAVE_AV_CONFIG_H -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -DPIC -std=c99 -fomit-frame-pointer -fPIC -fno-math-errno -fno-signed-zeros -fno-tree-vectorize 참고:컴파일 옵션이 복잡해 보이지만 그냥 configure에서 생성된 컴파일 옵션을 그대로 정리해 준 것 뿐입니다.
ffmpeg 폴더의 common.mak 파일을 열어보시면아래와 같은 부분이 있습니다. 1.%.o: %.c 2.$(CCDEP) 3.$(CC) $(CPPFLAGS) $(CFLAGS) $(CC_DEPFLAGS) -c $(CC_O) $< FFmpeg 컴파일 할 때, $(CPPFLAGS) $(CFLAGS) $(CC_DEPFLAGS) 이 세 개의 매크로에 정의된 옵션들을 사용하는 것을 알 수 있습니다.
ffmpeg 폴더의 config.mak 파일을 열어보시면 이 값들이 정의되어 있습니다.
CPPFLAGS는 아래와 같습니다. 1.CPPFLAGS= -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -DPIC 이 값들은 다 사용해 줍니다.
CFLAGS는 엄청 깁니다. 1.CFLAGS= -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -std=c99 -fomit-frame-pointer -fPIC -marm -g -Wdeclaration-after-statement -Wall -Wno-parentheses -Wno-switch -Wdisabled-optimization -Wpointer-arith -Wredundant-decls -Wno-pointer-sign -Wcast-qual -Wwrite-strings -Wtype-limits -Wundef -Wmissing-prototypes -Wno-pointer-to-int-cast -O3 -fno-math-errno -fno-signed-zeros -fno-tree-vectorize -Werror=implicit-function-declaration -Werror=missing-prototypes 복잡해 보이지만 하나씩 차근히 보면 정리가 됩니다. 여기서 -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -g -O3 옵션들은 다 뺍니다.우리는 Android ndk-build를 사용할 것이기 때문에 -O3 같은 최적화 관련 옵션은 지정하지 않습니다.(이것은 android build system이 알아서 해줍니다)-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon 이와 같은 cross compile관련, neon 관련 옵션도 빼줍니다.(이것은 나중에 Android.mk의 옵션으로 지정해 줄 것입니다)마지막으로 -W 로 시작하는 옵션은 warning 관련 옵션이니 그냥 다 뺍니다.
CC_DEPFLAGS는 별 것 없고 상관없는 값들입니다. 무시합니다.
추가로 subdir.mak 파일을 보시면 아래와 같은 부분이 있습니다. 1.$(OBJS) $(SUBDIR)%.ho $(SUBDIR)%-test.o $(TESTOBJS): CPPFLAGS += -DHAVE_AV_CONFIG_H $(OBJS) 에 정의된 모든 파일에 위 조건이 해당되므로 -DHAVE_AV_CONFIG_H 도 포함합니다.
이렇게 정리하면 위에서 한 줄로 정리한 COMMON_CFLAGS 컴파일 옵션들이 나옵니다.
컴파일 할 소스 파일들을 정의
이 부분은 소스가 좀 길고 복잡하게 느껴질 수 있습니다. 하지만 역시 핵심은 간단합니다.
먼저 FFmpeg의 Makefile을 하나만 분석해 보겠습니다. ffmpeg 폴더의 common.mak 파일을 열어보면 아래와 같은 부분이 있습니다. 1.OBJS += $(OBJS-yes) 컴파일에 사용할 소스 파일은 OBJS 매크로와 OBJS-yes 매크로에 정의되어 있다는 것을 알 수 있습니다. 우리도 이 소스들을 컴파일 하면 되므로 똑같이 적어줍니다.
이제 OBJS 매크로에는 xxxxx.o 와 같은 object 파일들이 쭉 저장되게 됩니다.이걸 그냥 간단히 전부 xxxxx.c로 변환해서 쓰면 가장 쉽겠지만 그렇게 간단하지는 않습니다.우선 c 파일 외에도 xxxxx.S 와 같은 어셈블리 코드들이 포함되어 있고,neon 컴파일 해야하는 소스들은 xxxxx.c.neon 또는 xxxxx.S.neon 과 같이 neon 접미사를 붙여줘야 하기 때문입니다.
다행인 것은, FFmpeg 소스들을 보면 neon 컴파일 해야 하는 소스들은 모두 _neon.c 와 같이 _neon 접미사가 붙어 있어서 이것으로 구분이 가능합니다.(ffmpeg/libavcodec/arm 폴더의 파일들을 훑어 보시기 바랍니다)따라서 _neon 접미사를 검색해서 해당 접미사가 있는 소스에만 .neon을 마지막에 추가해 주면 됩니다.
위와 같은 과정을 수동으로 일일이 진행하셔도 좋지만 번거로우니 Makefile 문법을 사용해 작성해 주면 됩니다.최종적으로 컴파일 할 소스 파일들은 각각 다음 매크로에 저장할 것입니다.
C_FILES: 컴파일 할 c 파일S_FILES: 컴파일 할 S 파일NEON_C_FILES: neon 컴파일 할 c 파일NEON_S_FILES: neon 컴파일 할 S 파일FFFILES: 컴파일 할 모든 소스 파일 전부 정의
이제까지 설명한 것을 종합해서 common.mk의 전체 소스를 보여드리면 아래와 같습니다. common.mk 파일을 다음과 같이 작성해줍니다.
ffmpeg/libavcodec 폴더의 Makefile을 열어 봅니다. 1.OBJS = allcodecs.o 2.audioconvert.o 3.avpacket.o 4.bitstream.o 5.bitstream_filter.o 6.dsputil.o 위와 같은 소스를 볼 수 있습니다.이는 다시 말하면 allcodecs.c, audioconvert.c … 와 같은 소스들은 컴파일 옵션과 상관없이 무조건 컴파일 하겠다는 뜻입니다.
다음으로 아래와 같은 코드들이 이어집니다. 1.OBJS-$(CONFIG_AANDCT) += aandcttab.o 2.OBJS-$(CONFIG_AC3DSP) += ac3dsp.o 3.OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o ffmpeg 폴더의 config.mak 파일을 열어서 CONFIG_AANDCT, CONFIG_CRYSTALHD 등을 찾아 보시면 이게 어떻게 돌아가는지 알 수 있습니다.config.mak 파일을 열어 보면 아래와 같이 되어 있습니다. 1.CONFIG_AANDCT=yes 2.!CONFIG_CRYSTALHD=yes 즉, “OBJS-$(CONFIG_AANDCT)”는 “OBJS-yes”로 변환되어 aandcttab.c 는 컴파일할 것이고,”OBJS-$(CONFIG_CRYSTALHD)”는 그렇지 않으니 crystalhd.c 는 컴파일 하지 않을 것 입니다.
이런 방법은 거의 모든 open source library에서 사용하고 있는 표준적인 방법이니 익숙해지는 것이 좋습니다.