ImageProcessing 에 사용된 기술(Adaptive Binarization)을 GPU로 처리해보자
2018, Jul 04
이번 포스팅에서 기존 영상처리에 사용되었던 기술인 Adaptive Binarization 을
CUDA 를 이용해 구현하는 방법을 알아볼 것이다.
그전에 extern “C” 선언에 대해서 알아보자.
보통 extern 선언은 외부 소스파일의 전역변수나 함수를 참조할 때 쓰는 선언이다. 함수의 경우 extern 선언을 하지 않고
쓸 수도 있다.
하지만 exter “C” 선언의 경우 의미가 약간 다르다.
extern “C” 란?
링커는 작업시 오브젝트간 함수 이용 및 위치를 알고 있어야 한다. 함수에 관한 이러한 정보는 컴파일러가 오브젝트 파일에 기록한다. 이러한 정보를 linkage라고 한다.
linkage 작업을 하면 함수이름 앞 또는 뒤에 ‘_’ 등의 심볼을 덧붙이게 된다. 그런데 C 와 C++ 은 컴파일 linkage 작업시 변수명, 함수명 등에 심볼을 기록하는 방식이 다르다.
왜냐하면 C 는 overloading 을 지원하지 않아 함수 이름이 유일한 반면, C++ 은 지원하기 때문에 인자의 개수와 데이터 타입에 대한 정보까지 넣어야 하기 때문이다.
그러므로 C 와 C++ 을 혼용하는 프로그램에서는 extern “C” 선언을 하여 각각의 방식으로 linkage 하도록 지시해야 한다.
extern “C” 선언은 C++ 방식이 아닌 C 방식으로 linkage 하도록 지시하는 역할을 한다.
CUDA 를 위한 환경설정
CUDA 소스 코드를 CUDA 컴파일러가 컴파일 할 수 있도록 Visual Studio 환경설정을 해보자.
프로젝트에서 마우스 오른쪽 클릭 후 빌드 종속성, 사용자 지정 빌드를 클릭 후
아래 그림처럼 CUDA 에 체크한다.
메뉴 - 프로젝트 - 프로젝트 속성 - C/C++ - 일반 - 추가 포함 디렉터리
이 곳에 $(CUDA_PATH)/include 를 추가한다. 추가하지 않아도 자동입력 되어있을 수 있다.
링커 - 일반 - 추가 라이브러리 디렉터리
여기에 $(CUDA_PATH)/Win32 를 추가한다. CUDA에 사용되는 라이브러리 폴더 경로를 설정하는 작업이다.
링커 - 입력 - 추가 종속성
추가적으로 사용할 라이브러리를 입력한다. cudart_static.lib 를 입력한다.
도구 - 옵션 - 텍스트 편집기 - 파일 확장명
여기에 cu 와 cuh 확장명을 등록하고 Microsoft Visual C++ 을 편집환경으로 만든다.
.cu 확장자로 소스파일 만들고 속성 - 구성속성 - 일반, 속성 - 구성속성 - CUDA C/C++ - common 을 다음과 같이 세팅한다.
개발 시작 // 먼저 큰 그림, 구조는 무엇일지 생각해보자.
CUDAImageProc.cu 파일에서 정의한 함수는 ImageProc.cpp 에서 콜한다.
ImageProc.cpp 에 CUDA 함수를 콜하는 헬퍼 함수를 정의한다.
CUDAImageProc.cu 파일에서 kernel 함수를 정의하고 kernel 함수 콜을 돕는 헬퍼함수도 정의한다.
CUDA 관련 기능은 모두 CUDAImageProc.cu에 정의한다.
ImageProcessingDoc.cpp 에 이벤트처리기를 만들고 ImageProc.cpp 의 함수를 콜한다.
CUDAImageProc.cu 에 GPU 메모리 할당 할 변수를 선언하고 GPU 메모리를 세팅하는 기능을 만들어보자
CUDAImageProc.cu
ImageProc에 메모리를 할당하고 반환하는 작업을 도와줄 함수를 만들어보자.
ImageProc.h
기존과는 다르게 이번에 정의한 함수는 static 선언이 붙지 않았다.
즉, 객체를 생성해야 사용할 수 있다.
ImageProc.cpp
이제 Adaptive Binarization 수행하는 기능을 만들어보자.
CUDAImageProc.cu
커널함수를 정의한다. 블락 index와 thread index 를 이용해 절대 thread index를 알아내는 것이 관건이다.
CUDAImageProc.cu
ImageProc.cpp 에 위 기능을 사용할 수 있는 함수를 정의하자
ImageProc.h
ImageProc.cpp
이벤트 처리기를 단다. 기존과 다르게 사용할 ImageProc 함수가 static 이 아니기 때문에
ImageProc 객체를 만들어서 사용한다.
ImageProcessingDoc.h
Doc 클래스가 생성될 때 초기화하고, 소멸할 때 반환하며
새로운 Document가 생성될 때 기존 객체를 삭제하고 새로운 객체를 생성한다.
ImageProcessingDoc.cpp
이벤트 처리기를 달고 시간을 측정해보자
ImageProcessingDoc.cpp
실행하면 다음 그림과 같이 되고
시간은 CUDA 프로그래밍 적용 했을 때 : 2.74454초
CUDA 프로그래밍 적용 하지 않았을 때 : 7.01979초
로 CUDA 프로그래밍이 더 빠른 것을 확인 할 수 있다.