2019년 9월 10일 화요일

우분투 OpenCV 설치 및 스테레오 이미지에서 뎁스 맵 생성 방법

우분투 18.04 기반 OpenCV에서 스테레오 이미지를 이용한 뎁스맵(depthmap) 생성 방법을 간략히 정리한다. 이 글에서는 아나콘다 환경에서 설치하는 방법과 그 외의 환경에서 설치하는 방법으로 구분하였다. 

아나콘다 환경 설치
아나콘다 환경에서 설치하는 방법은 다음 링크를 참고하였다. 
아나콘다는 source activate <environment-name> 으로 환경 설정후 설치해야 한다.  설치 방법은 다음과 같다. 
pip install opencv-python
pip install opencv-contrib-python

소스코드 빌드 및 설치 방법
빌드해 직접 설치하는 방법은 다음 링크를 참고하였다. 
다음과 같이 업데이트한다.
sudo apt-get update
sudo apt-get upgrade

다음과 같이 패키지를 설치한다.
sudo apt-get install build-essential cmake
sudo apt-get install pkg-config
sudo apt-get install libjpeg-dev libtiff5-dev libpng-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libxvidcore-dev libx264-dev libxine2-dev
sudo apt-get install libv4l-dev v4l-utils
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev 
sudo apt-get install libgtk2.0-dev
sudo apt-get install mesa-utils libgl1-mesa-dri libgtkgl2.0-dev libgtkglext1-dev  
sudo apt-get install libatlas-base-dev gfortran libeigen3-dev
sudo apt-get install python2.7-dev python3-dev python-numpy python3-numpy

소스코드를 다운받을 폴더를 만든다.
mkdir opencv
cd opencv

소스코드를 받는다.
wget -O opencv.zip https://github.com/opencv/opencv/archive/4.0.1.zip
unzip opencv.zip

wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.0.1.zip
unzip opencv_contrib.zip

빌드 폴더를 만든다.
cd opencv-4.0.1/
mkdir build
cd build

메이크 파일을 생성한다.
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D WITH_TBB=OFF \
-D WITH_IPP=OFF \
-D WITH_1394=OFF \
-D BUILD_WITH_DEBUG_INFO=OFF \
-D BUILD_DOCS=OFF \
-D INSTALL_C_EXAMPLES=ON \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D BUILD_EXAMPLES=OFF \
-D BUILD_TESTS=OFF \
-D BUILD_PERF_TESTS=OFF \
-D WITH_QT=OFF \
-D WITH_GTK=ON \
-D WITH_OPENGL=ON \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.0.1/modules \
-D WITH_V4L=ON  \
-D WITH_FFMPEG=ON \
-D WITH_XINE=ON \
-D BUILD_NEW_PYTHON_SUPPORT=ON \
-D PYTHON2_INCLUDE_DIR=/usr/include/python2.7 \
-D PYTHON2_NUMPY_INCLUDE_DIRS=/usr/lib/python2.7/dist-packages/numpy/core/include/ \
-D PYTHON2_PACKAGES_PATH=/usr/lib/python2.7/dist-packages \
-D PYTHON2_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython2.7.so \
-D PYTHON3_INCLUDE_DIR=/usr/include/python3.6m \
-D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include/  \
-D PYTHON3_PACKAGES_PATH=/usr/lib/python3/dist-packages \
-D PYTHON3_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6m.so \
../

라이브러리를 메이크한다.
make -j4
sudo make install

라이브러리 경로를 확인하고 없으면 추가한다.
cat /etc/ld.so.conf.d/*
sudo sh -c 'echo '/usr/local/lib' > /etc/ld.so.conf.d/opencv.conf'
sudo ldconfig

파이썬을 위한 cv2 라이브러리를 복사한다.
sudo cp /usr/local/python/cv2/python-2.7/cv2.so /usr/local/lib/python2.7/dist-packages/
sudo cp /usr/local/python/cv2/python-2.7/cv2.so /usr/lib/python2.7/dist-packages/
sudo cp /usr/local/python/cv2/python-3.6/cv2.cpython-36m-x86_64-linux-gnu.so /usr/lib/python3/dist-packages
sudo cp /usr/local/python/cv2/python-3.6/cv2.cpython-36m-x86_64-linux-gnu.so /usr/local/lib/python3.6/dist-packages

다음과 같이 파이썬에서 버전을 확인한다. 버전이 나오면 제대로 설치된 것이다. 
python
import cv2
cv2.__version__

이 경우, 다음과 같이 예제를 실행해 보면 잘 수행되는 것을 확인할 수 있다.
facedetect 실행 결과

video cam 테스트 화면

뎁스맵 생성
뎁스맵 생성을 위해 다음 코드를 입력해 실행한다. 
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
imgL = cv.imread('tsukuba_l.png',0)
imgR = cv.imread('tsukuba_r.png',0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imgL,imgR)
plt.imshow(disparity,'gray')
plt.show()

아래와 같이 2개의 이미지를 입력하면, 뎁스맵을 얻을 수 있다.

뎁스맵을 계산하기 위해서는 다음과 같이 몇 가지 파라메터가 필요하다.
여기서, P는 두개의 카메라 O, O' 지점에서 본 동일 특징점이다. f는 focal length이며, Z는 실제 O와 P의 거리이다. B는 O와 O'사이의 거리가 된다. 이 경우, 다음과 같은 수식을 세울 수 있다. 이 경우, Pu와 Pu' 차이(disparity)는 다음과 같이 계산할 수 있다.
 
disparity = Pu - Pu' ∝ (B⋅f) / z

OpenCV에는 이 disparity와 깊이를 계산해 주는 함수를 내장하고 있다. 참고로, 다음은 이를 자동으로 계산해 주는 웹사이트이다. 


이 수식으로 나오는 disparity(이격거리)에 대한 깊이값은 이론적인 값임에 주의해야 한다. 실제 실측한 거리는 이보다 더 큰 오차를 가진다. 이는 대상 특징점을 구하는 알고리즘, 카메라에 반사된 대상물의 색상 및 조도에 따라 결과가 크게 달라질 수 있기 때문이다. 앞의 수식에서 좌우 이미지에 유일한 P 특징점을 얻는 것은 쉽지 않으며, 촬영 카메라, 환경 등에 따라 크게 달라질 수 있다. 

참고로, 스테레오 이미지에서 뎁스맵을 계산하는 방법은 다음을 참고한다.
StereoBM함수를 이용하면, 틈이 듬성듬성 비어 있는 엉성한 뎁스맵 결과를 얻게 된다. 이를 필터를 적용해 후처리하면, 매끄러운 결과를 얻을 수 있다.
StereoBM 결과
후처리 결과

다음은 이와 관련된 처리 코드이다(참고 - Disparity map post-filtering).
    Mat left  = imread(left_im ,IMREAD_COLOR);
    if ( left.empty() )
    {
        cout<<"Cannot read image file: "<<left_im;
        return -1;
    }
    Mat right = imread(right_im,IMREAD_COLOR);
    if ( right.empty() )
    {
        cout<<"Cannot read image file: "<<right_im;
        return -1;
    }

    max_disp/=2;
    if(max_disp%16!=0)
         max_disp += 16-(max_disp%16);
    resize(left ,left_for_matcher ,Size(),0.5,0.5, INTER_LINEAR_EXACT);
    rsize(right,right_for_matcher,Size(),0.5,0.5, INTER_LINEAR_EXACT);

    Ptr<StereoBM> left_matcher = StereoBM::create(max_disp,wsize);
    wls_filter = createDisparityWLSFilter(left_matcher);
    Ptr<StereoMatcher> right_matcher = createRightMatcher(left_matcher);
    cvtColor(left_for_matcher,  left_for_matcher,  COLOR_BGR2GRAY);
    cvtColor(right_for_matcher, right_for_matcher, COLOR_BGR2GRAY);
    matching_time = (double)getTickCount();
    left_matcher-> compute(left_for_matcher, right_for_matcher,left_disp);
    right_matcher->compute(right_for_matcher,left_for_matcher, right_disp);
    matching_time = ((double)getTickCount() - matching_time)/getTickFrequency();

    wls_filter->setLambda(lambda);
    wls_filter->setSigmaColor(sigma);
    filtering_time = (double)getTickCount();
    wls_filter->filter(left_disp,left,filtered_disp,right_disp);
    filtering_time = ((double)getTickCount() - filtering_time)/getTickFrequency();

    Mat raw_disp_vis;
    getDisparityVis(left_disp,raw_disp_vis,vis_mult);
    namedWindow("raw disparity", WINDOW_AUTOSIZE);
    imshow("raw disparity", raw_disp_vis);
    Mat filtered_disp_vis;
    getDisparityVis(filtered_disp,filtered_disp_vis,vis_mult);
    namedWindow("filtered disparity", WINDOW_AUTOSIZE);
    imshow("filtered disparity", filtered_disp_vis);
    if(!solved_disp.empty())
    {
        Mat solved_disp_vis;
        getDisparityVis(solved_disp,solved_disp_vis,vis_mult);
        namedWindow("solved disparity", WINDOW_AUTOSIZE);
        imshow("solved disparity", solved_disp_vis);
        Mat solved_filtered_disp_vis;
        getDisparityVis(solved_filtered_disp,solved_filtered_disp_vis,vis_mult);
        namedWindow("solved wls disparity", WINDOW_AUTOSIZE);
        imshow("solved wls disparity", solved_filtered_disp_vis);
   }
   while(1)
   {
        char key = (char)waitKey();
        if( key == 27 || key == 'q' || key == 'Q') // 'ESC'
            break;
   }

레퍼런스

댓글 없음:

댓글 쓰기