반응형

Computer Vision 이미지 자료형

배경

Python2 시절까지만 해도 성능을 중요시했던 컴퓨터 비젼 분야는 오랫동안 C/C++ 계열이 메인이었는데, 지금은 컴퓨터 비젼을 한다면 Python도 필수로 해야 하는 시대가 되었다. 이건 딥러닝 파급의 효과가 가장 컸다. 다만, 최근에 내가 놓친 부분을 알기위해서 빠른 지식 습득 수단으로 강의를 듣고 있는데, 강의에서도 그렇고 회사에서 기술이전으로 로봇공학쪽 박사 포닥중인 강의 하시는 분도 그렇고 그냥 다르니 주의해라는 듯이 설명해서 내 입장에서는 난감했다. 사실 본래 자료형이 만들어진 목적이 있기 때문이다.

그래서 끄적여 본다.

Python 이미지 처리 모듈

기본적으로 이미지의 첫 출발은 이미지를 메모리에 올리는 것 부터 시작이 된다. Python 에서는 대표적인 모듈이 3가지가 존재한다. PIL, Scikit Image, OpenCV. 이중에서 현재는 OpenCV를 많이 사용하고 있다.다만, 대부분 이 라이브러리들은 이미지 파일을 로드하게 되면 numpy 모듈의 array로 로드를 하는 특징이 있다.

PIL

Python Image Library 로 초기 Python 영상처리 많이 사용했다. 필자도 OpenCV 보다 PIL 을 주로 썼었는데, 이는 OpenCV의 경우 BGR로 로드하는 반면 이 라이브러리는 RGB로 로드가 되었기 때문에 이런 변환 코드를 덜짜기 위해서 썼었다. 파이썬은 성능보다 생산성을 추구하였으니 말이다. 문제는 PIL 이 현재는 유지보수가 잘 안되고 있다. 지금의 PIL는 fork 된 프로젝트 중에서 호환만 경우 유지되고 있는 수준이다.

Scikit Image

Scipy 기반으로 제공되는 라이브러리이다. Scipy는 SCI 이름에서 알 수 있듯이 연구 용도로 많이 사용한다. 경우에 따라 Scipy 는 상당히 높은 수준의 연산을 도와주는 모듈들이 있다. 그러나 필자를 포함해서 널리 사용되지는 않는 것 같다.

OpenCV

인텔에서 개발하다가 오픈 소스로 전환된 라이브러리다. 이미지 로드시 BGR로 메모리에 올라가는 것을 특징으로 갖는데 이는 초기에 성능을 위해서 인텔 CPU가 리틀엔디안 방식으로 데이터를 처리하기 때문에 일부러 뒤집은 탓이다. OpenCV1 시절은 C, OpenCV2 시절은 C/C++, OpenCV3 에서는 Java/Python 이런식으로 확장되어 왔다. Mat 이라는 자체 이미지 자료구조를 가지고 있었는데, Python 에서는 numpy.array 자료구조를 사용한다.

개인적으로 numpy.array로 로드 되는 것을 처음 알았을 때는 기존 Mat 보다 이미지 정보가 적어서 불편하다 생각했지만, 수학적인 연산도 자연스럽게 사용할 수 있었고 그렇지 않아도 느린 버전업이 빨라 졌다. 즉 버그 픽스가 빨라졌다는 것이다. numpy 경우 Matlab 처럼 멀티 스레드로 메트릭스 연산을 하기 때문에 C로 단일 스레드로 연산한것 보다 빠르다.

numpy.array

numpy 모듈은 태생이 수학자들이 만든 C로 만들어진 모듈들이다. Python 이 런타임이 느린 언어이지만, 이 모듈로 간단히 몇 줄로 크게 뒤지지 않는 병렬 계산이 가능하다. 게다가 구조도 직관적이기도 하다.

다만 문제는 태생이 메트릭스 연산을 위해서 탄생한 구조이다보니 나중에 컴퓨터 비젼관련 라이브러리에서 다룰때 종종 헷깔리게 된다. 이미지에서의 크기의 개념은 (가로 x 세로)이나 메트릭스에서는 (행, 열) 개념이기 때문이다. 이미지의 값을 numpy에 넣게되면 (세로, 가로) 느낌으로 사용하게 된다.

이때문에 초보자들은 여기서 많이 틀린다. 문제는 내가 모든 강의를 들은 것은 아니지만 대부분 주의 하고 외우세요 하면서 가르치시더라. 태생이 원래 수학적 접근의 차이라 다른것 뿐이지만 말이다. 사실 각 모듈의 태생의 목적을 알면 헷깔릴 일이 별로 없다. 깜박해도 금방 생각날테니 말이다.

그렇다 이 말 하려고 어그로 끌었다. "모듈의 태생을 알면 이유를 알 수 있다"

이제 배경을 알았으니 array shape을 받을 때 rows, cols, dim = np.array().shape 형태로 사용하는 것이 좀 더 덜 헷깔릴 것이다. 작지만 유용한 팁이라 할 수 있다.

덤 OpenCV 가 cv2 인 이유

덤으로 이글을 쓰는 시기에 OpenCV4가 사용되고 있다. 그럼에도 Python 에서는 cv2 라는 모듈 네임을 사용하고 있다. 대체 왜 그럴까? cv2 부터 C++이 도입되면서 OpenCV 코드들이 객체화가 되었다. 그리고 Mat 이라는 class 를 사용했었는데, Python 에서는 Mat 대신 numpy를 사용하지만, 둘이 사용법이 비슷하고 굳이 cv3 처럼 올릴 필요가 없다. 객체지향으로 개발하는 이유가 원래 이런 것들을 유지하려고 했던것이 아니었겠는가? 그리고 성능을 위해서 알고리즘은 여전히 C++로 구현되고 Java를 포함해서 Python에 wrapper 된 경우도 많다. 그리고 CV2라는 네임스페이스는 C++이 쓰는 것 아니었는가?

반응형
반응형

개요

ubuntu 20.04 LTS 에서 OpenCV C++ 기본 빌드 환경에 대해서 샘플로 작성한 포스트. 해당 샘플은 카메라로 받은 이미지를 OpenCV imshow() 를 통해서 보여준다.

기본 틀이기 때문에 처음에는 복붙으로 확인이후 자신이 개발하고자 하는 것에 맞춰서 수정해 나가면 된다. 물론 makefile 기반이다.

굳이 camera 코드를 샘플로 작성한 이유는 임베디드 환경에서 카메라 다루게 되는 경우 성능상 C++을 써야 하기 때문이다. 특별한 이유가 없다면 가능하면 그냥 opencv-python 을 사용하자(하지만 제품을 만들려면 저사양 고성능을 뽑아야 하느라 삽질하겠지).

makefile sample

makefile 에 대해서 배운적이 없다면 복붙전에 읽어보고 사용하는 것이 좋다. 파일명은 반드시 makefile로 지어야 한다.  make clean 을 할 경우 규칙에 의해서 빌드 결과와 실행 파일이 삭제 되도록 되어 있다.

# Makefile
CPPFLAGS = -std=c++14 $(shell pkg-config --cflags opencv4)
LDLIBS = $(shell pkg-config --libs opencv4)
# source file name
OBJS = webcam_sample.o
# result execute file name
TARGET = test

all: $(TARGET)

$(TARGET): $(OBJS)
	g++ $(CPPFLAGS) -o $@ $< $(LDLIBS)

clean:
	rm -f *.o
	rm -f $(TARGET)

camera sample code

널리 알려져 있는 camera sample code에서 실행시 카메라 번호를 수정할 수 있도록 약간만 수정한 소스코드이다. 

// webcam_sample.cc
#include <opencv4/opencv2/videoio.hpp>
#include <opencv4/opencv2/highgui.hpp>
#include <iostream>
#include <string>

bool is_number(const std::string& s)
{
// 
    return !s.empty() && std::find_if(s.begin(), 
        s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
}

int main(int argc, char** argv)
{
    int cam_num = 0;
    if (2 == argc) {
        auto camNumParam = std::string(argc[1]);
        if (is_number(camNumParam)) {
            cam_num = std::atoi(camNumParam);
        }
    }

    cv::VideoCapture cap(cam_num);
    if(!cap.isOpened()) {
        std::cout << "did not open camera" << std::endl;
        return -1;
    }
    cv::Mat frame;
    while(1)
    {
        cap >> frame;
        cv::imshow("Camera Capture", frame);
        if (cv::waitKey(30) >= 0)
        {
            break;
        }
    }
    return 0;
}

카메라 장치 다룰때 주의사항

우분투는 먼저 인식한 카메라 장치 순으로 번호를 부여한다. 일반적으로 웹에 돌아다니는 예제는 0번으로 되어 있지만, 노트북같은 환경에서 usb 카메라를 붙이면, 노트북 기본 카메라가 0번, usb web cam은 1번으로 잡히게 된다.

또한 다른 프로그램에서 카메라를 점유(사용)하고 있을 경우에는 다른 프로그램에서 접근을 할 수 없다.

반응형

'Computer Vision' 카테고리의 다른 글

Computer Vision 이미지 자료형  (0) 2024.01.18
Ubuntu 20.04 OpenCV 4.x + cuDNN 설치  (0) 2023.04.26
반응형

개요

OpenCV 에서는 오래전부터 nVidia CUDA를 사용할 수 있도록 해줬다. 그중 DNN 모듈은 기존 딥러닝 플랫폼인 텐서플로나 파이토치 같은 모델을 변형하여 사용할 수 있다.

이때 cuDNN을 통해서 그래픽 카드를 활용하면 기존 cpu를 사용한 것 보다 좀 더 빠른 연산을 수행할 수 있다. 이 과정은 일반적은 OpenCV 빌드보다 신경써줘야 할 것이 많다.

서버 환경이 아닌 소형 임베디드 환경을 전제로 하여 이글을 작성하는 시점에 가장 널리 사용중인 Ubuntu 20.04 desktop기준 개발환경으로 서술한다.

만약 nVidia의 jetson 시리즈 보드를 사용할 경우 기본적으로 openCV가 빌드되어 있다.

기존 OpenCV 삭제

기본적으로 python3 pip를 통해서도 openCV 라이브러리가 설치 되어 있기도 하다. ros를 설치 했을 경우에도 기본적으로 opencv가 함께 설치 된다. 그리고 이미 설치가 되어 있더라도 cuDNN가 지원이 되지 않다면 이 또한 새로 빌드를 해야 한다.

# 기존 opencv 4.x 설치 확인
pkg-config --modversion opencv4

# 기존 opencv 3.x 이하 설치 확인
pkg-config --modversion opencv

# uninstall opencv-python
pip3 uninstall opencv-python

# ros를 통해서 설치가 되어 있는 경우
sudo apt purge ros-*
sudo apt autoremove

# 과거에 source code를 직접 빌드해서 설치했었을 경우
sudo find /usr/local -name "*opencv*" -exec rm -i {} \;

위의 명령어를 순서대로 해도 상관이 없지만, 먼저 확인을 하고 순차적으로 진행하는 것을 권장한다.

그래픽 드라이버 설치

그래픽 드라이버 설치를 할 경우 ubuntu desktop일 경우 super 키로 검색하여 "addtional drivers" 창에서 설정을 해도 상관은 없다. 다만 설치를 할경우 meta 라고 표기 되어 있는 것을 설치해야 모니터 외에 연산을 용도로 그래픽 카드를 사용할 수 있다.

# 일반적인 desktop 환경의 경우
sudo apt update
sudo apt install cuda-dirvers

# desktop이 아닌 다른 장치의 경우
sudo lspci -v | less
sudo ubuntu-drivers devices

# 해당 경우는 rtx3090 경우 저장소 추가하여 설치하는 예시
sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update
sudo apt-get install nvidia-driver-460

# 설치를 완료한 뒤에는 reboot 할 것
reboot

정상적으로 설치가 되었을 경우 아래의 명령어를 실행했을 때 그래픽 카드에 대한 정보가 출력이 되어야 한다. 만약 출력이 되지 않는다면, 제대로 그래픽 카드 드라이버가 설치 되지 않은 것이다.

nvidia-smi

그래픽 카드 정보를 보면 CUDA 다음 버전이 있는데, CUDA 버전을 의미하는 것이 아니라 사용 가능한 버전을 표시되는 것이다.

CUDA 설치

nVidia 웹에서 CUDA를 설치하려고 하면 최신 CUDA로 다운로드 받도록 유도하는데 최신 버전의 경우 안정화 지원이 덜되는 경우가 많기 때문에 가능하면 최신화 이전 버전을 설치하는 것이 좋다. 아래 링크에 접속하여 CUDA 버전을 고르고 OS 등에 맞춰서 local로 설치를 하려고 하면, 터미널에 입력하는 명령어가 표시된다 이를 따라서 진행하면 된다.

https://developer.nvidia.com/cuda-toolkit-archive

 

CUDA Toolkit Archive

Previous releases of the CUDA Toolkit, GPU Computing SDK, documentation and developer drivers can be found using the links below. Please select the release you want from the list below, and be sure to check www.nvidia.com/drivers for more recent production

developer.nvidia.com

cuDNN 설치

cuDNN은 딥러닝같은 연산을 최적화 시켜주는 라이브러리다. 시간이 지나면, 설치 패키지로 배포가 되겠지만, 아직은 직접 다운로드 받아서 복사를 해줘야 한다(복사가 설치다). 앞에 설치한 CUDA 버전에 맞춰서 다운로드 받아으면 된다.

https://developer.nvidia.com/rdp/cudnn-archive

 

cuDNN Archive

Download releases from the GPU-accelerated primitive library for deep neural networks.

developer.nvidia.com

압축 파일을 다운로드가 완료가 되었다면 압축을 해제하고 각 필요한 파일들을 맞는 위치에 복사를 한뒤에 권한을 설정해주면 된다. 이때 앞의 CUDA 버전에 맞춰서 맞는 폴더로 복사를 하면 된다. 아래 예시는 cuda-11.8 을 설치했을 경우이다.

tar -xvf cudnn-linux-x86_64-8.8.1.3_cuda11-archive.tar.xz
cd cudnn-linux-x86_64-8.8.1.3_cuda11-archive
sudo cp include/cudnn*.h /usr/local/cuda-11.8/include
sudo cp -P lib/libcudnn* /usr/local/cuda-11.8/lib64
sudo chmod a+r /usr/local/cuda-11.8/include/cudnn*.h /usr/local/cuda-11.8/lib64/libcudnn*

update-alternatives

gcc 같은 빌드 툴들이 여러버전이 설치 되었을 경우 디폴트로 실행하도록 할 필요가 있다. 이 경우 전통적으로 심볼 링크를 사용하는데, 이를 관리해주는 update-alternatives 를 활용하면 억지로 심볼링크를 쓸 필요가 없다. 아래의 예제는 cuda-11.8 일 경우를 전제로 작성한다.

# update-alternatives config 확인
sudo update-alternatives --config cuda

# 아무것도 없는 경우
sudo update-alternatives --install /usr/local/cuda cuda /usr/local/cuda-11.8 11

# update-alternatives config 재확인
sudo update-alternatives --config cuda
# 추가된 항목이 보이게 됨

전통적인 심볼링크

위의 update-alternatives 가 없거나 자신이 상남자라고 생각한다면 전통적인 방식으로 직접 심볼링크로 해도 상관은 없다.

# 기존 심볼링크 제거
sudo rm /usr/local/cuda
# 새 심볼링크 생성
sudo ln -s /usr/local/cuda-11.8 /usr/local/cuda

nvcc 확인

터미널에서 nvcc가 정상적으로 작동되는지 확인한다. 표시되는 정보에 설치된 CUDA와 같은 버전이 표시되어야 한다. 만약 nvcc가 실행되지 않는다면 환경변수를 아래와 같이 설정해준다. 그리고 이 터미널에서 opencv를 빌드해야 한다.

# nvcc 정보 확인
nvcc -V

# nvcc -V 정보가 안보일 경우
export PATH=$PATH:/usr/local/cuda/bin
export CUDADIR=/usr/local/cuda
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64

컴파일러 변경

opencv4.x 빌드시 gcc/g++ 버전이 너무 높으면 빌드가 되지 않는다. Ubuntu 20.04 LTS desktop 에 기본적으로  gcc-9 버전이 설치되어 있다. 이 경우 빌드를 시도하면 gcc-8, g++-8 이상은 빌드할 수 없다는 에러메시지를 볼 수 있다. 따라서 gcc-7, g++-7을 설치하자.

sudo apt install -y gcc-7 g++-7

이렇게 설치한 뒤에도 여전히 gcc-9와 g++-9가 default로 되어 있기 때문에 update-alternatives를 이용해서 바꿔주자

# 변경 및 우선순위 확인
sudo update-alternatives --config gcc
sudo update-alternatives --config g++

# 누락 되었을 경우
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 7
sudo update-alternatives --instlal /usr/bin/g++ g++ /usr/bin/g++-7 7

역시 update-alternatives가 없거나(없으면 설치하면 됨), 자신이 상남자라면 직접 심볼링크를 생성해서 사용해도 상관 없다. 물론 평범한 겁쟁이라면 아래 방법은 가능하면 피하자.

sudo rm /usr/bin/gcc /usr/bin/g++ /usr/bin/cc /usr/bin/c++
sudo ln -s /usr/bin/gcc-7 /usr/bin/gcc
sudo ln -s /usr/bin/g++-7 /usr/bin/g++
sudo ln -s /usr/bin/gcc-7 /usr/bin/cc
sudo ln -s /usr/bin/g++-7 /usr/bin/c++

# 버전확인
gcc --version

gcc 와 g++ 버전이 7로 보인다면 이제 다음단계로 넘어가면 된다.

VTK 설치

OpentCV가 viz 부분을 VTK 모듈을 의존한다. 만약 사용하지 않는다면 OpenCV 빌드시 해당 부분을 off 하고 빌드를 해도 된다.

sudo apt install vtk7

GTK 2.0 이상 설치

OpenCV가 리눅스에서 imshow() (image show) 같은 독자적인 창을 생성할 경우 사용하는 의존 모듈이다. 만약 개발중이 아니거나 중간중간 로컬에서 이미지를 표시하지 않는다면, OpenCV를 빌드할때 해당 옵션들을 off 한 상태에서 빌드하면 된다. 버전은 최소 2.0 이상이 필요하다.

일반적인 개발환경이라면, 해당 모듈도 미리 설치를 해주자.

sudo apt install -y libgtk2.0-dev

OpenCV 빌드

다운로드

이제 OpenCV 소스코드를 다운로드 받을 차례이다. OpenCV 본가 외에 contrib 모듈이 존재하는데 해당 모듈은 beta 수준의 모듈들을 담고 있다. 충분한 시간이 지나면 OpenCV 본 소스에 편입이 되지만, 최신 기능을 사용하기 위해서는 contrib가 필수 적이다.

둘다 버전에 맞춰서 다운로드를 받아야 한다. 예시에서는 4.6.0 버전으로 받는데 url 체계는 같으므로 존재하는 버전이면 숫자만 바꿔서 wget으로 다운로드 받아도 된다.

cd ~
wget -O opencv-4.6.0.zip https://github.com/opencv/opencv/archive/4.6.0.zip
wget -O opencv-contrib-4.6.0.zip https://github.com/opencv/opencv_contrib/archive/4.6.0.zip
unzip opencv-4.6.0.zip
unzip opencv-contrib-4.6.0.zip

압축을 풀는 과정에서 opencv-contrib-4.6.0.zip 이 생성하는 폴더이름이 opencv_contrib-4.6.0로 바뀌는 경우 있으니 주의 하자.

CMake-gui

대부분 CMake로 makefile을 생성할 경우 스크립트를 만들어서 사용을 한다. 하지만, 직관적으로 어떤 옵션을 빼고 넣어야 하는지 확인하기 어렵기 때문에 여기서는 CMake-gui 를 사용한다. 우선 CMake-gui를 설치하도록 하자.

sudo apt install -y cmake cmake-gui

cmake-gui 를 실행하기 전에 소스파일 폴더 안에 build 폴더를 먼저 만든다. 여기서는 opencv 4.6.0을 다운로드 받아서 진행하는 것을 전제로 한다.

cd ~/opencv-4.6.0
mkdir build

이 터미널은 아직 닫으면 안된다.

super키(윈도키) 를 누른뒤 cmake-gui 를 실행한다. 처음 cmake-gui 를 실행한다면 공란으로 보이는데 우선 where is the source code 경로를 ~/opencv-4.6.0 으로 직접 입력하거나 Browse source 버튼으로 위치를 찾아도 된다. 경로는 가능하면 절대경로로 작성하는 것이 좋다.

where to build the binaries 항목은 아까 만든 ~/opencv-4.6.0/build 경로로 잡는다. 이후에 하단의 configure 버튼을 클릭하면, cmakelist.txt를 찾으면서 초기 설정들을 찾아서 표시해준다.

이제 옵션을 수정하면 된다. JAVA로 개발하지 않는다면, search에 java를 입력해서 해당되는 체크표시를 해제하면 된다. 우리가 지금 빌드하는 목적은 그래픽 카드를 이용해서 opencv DNN 모듈을 사용하려는 거니 다음 항목들을 체크한다.

  • WITH_CUDA
  • BUILD_CUDA_STUBS
  • OPENCV_DNN_CUDA

이외에 유료 모듈과 pkg-config 를 설치 되게 위해서 아래 항목들도 체크 표시를 해준다.

  • OPENCV_ENABLE_NONFREE
  • OPENCV_GENERATE_PKGCONFIG

그리고 시행착오로 에러메시지를 통해 삽질(?)로 알게된 옵션을 추가한다. 옵션을 추가하는 방법은 add entry 를 통해서 추가하면 된다.

옵션라벨 타입
CUDNN_INCLUDE_DIR PATH /usr/local/cuda/include
CUDNN_LIBRARY PATHFILE /usr/local/cuda/lib64/libcudnn.so
CUDNN_VERSION STRING 8.8
OPENCV_EXTRA_MODULES_PATH PATH ~/opencv_contrib-4.6.0/modules

opencv-python 모듈처럼 하나의 통합된 형태의 라이브러리를 사용하는 것이 아니면, BUILD_opencv_world 항목도 찾아서 체크를 해제하면 된다.

모든 설정을 마쳤으면 configure 버튼을 다시 클릭한다. 설정을 적용하는 과정에서 필요한 파일을 다운로드 받기도 한다. 아무런 에러가 없이 무사히 완료가 되었다면, generate 버튼을 클릭하여 빌드 파일을 생성한다. 여기까지 무사히 마쳤다면 cmake-gui 창은 닫아도 된다.

make & install

이제 다시 터미널에서 생성된 파일들이 있는 build 폴더로 이동한다. make를 입력하여 빌드를 시작하면 된다. 만약 빌드 시간을 측정하고 싶다면 time make 처럼 입력해도 된다. 컴퓨터 성능에 따라서 걸리는 시간의 차이가 있다.

cd ~/opencv-4.6.0/build
# 큰무리가 없다면
make

# 만약 빌드 환경의 메모리가 부족하다면 $(nproc) 보다 작은 숫자
make -j $(nproc)

만약 메모리가 부족할 것 같다면, -j 옵션으로 job(사실상 thread) 수를 줄여서 빌드하면 된다. 대신 시간이 더 걸린다.

무사히 빌드를 마쳤다면 이제 라이브러리 설치를 하면 된다.

sudo make install

정상적으로 마친뒤에 설치 여부를 확인한다.

pkg-config --modversion opencv4

opencv 버전이 표시가 된다면 정상적으로 설치가 된 것이다. pkg-config 는 c++ 코드를 빌드할때 필요한 존재니 반드시 있어야 한다.

참조

 

 

OpenCV + CUDA 직접 빌드하기 (Windows/Linux 종합)

최근에 opencv에 있는 dnn을 한번 써보려고 직접 소스를 받아서 빌드(build)해 보았다. 역시나 엄청난 삽질의 연속이고 할 때마다 이것 저것 해결책을 검색하느라 많은 시간을 소모한다 (삽질은 누구

darkpgmr.tistory.com

 

OpenCV Building with CUDA CUDNN - No CUDNN

Hi, It seems that you already find in in this comment: For Jetson 4.5, include path is /usr/include/ and the library path is /usr/lib/aarch64-linux-gnu/. Thanks.

forums.developer.nvidia.com

 

반응형

+ Recent posts