Intel C++ 컴파일러 11.1을 설치하고, 정수형 Vector Class를 이용하여 이미지 처리를 병렬화 하는 프로그램을 구현해 봤습니다.
성능은 대략 Intel Core2Duo E8400 3.0G에서 약 5~6배의 성능 향상 효과를 보였습니다.
테스트를 위해 사용한 이미지는 lena.bmp 512 x 512 grayscale 이미지입니다.
원본 이미지
Histogram stretching 결과 이미지
일반적인 SISD(Single Instruction Single Data)형태로 Histogram stretching을 구현한것과 SIMD 형태로 구현한 코드의 core 부분의 차이를 보십시오.
//영상의 histogram에서 최소값과 최대값을 구하는 함수 int imageProcess::getMinMax(int* min, int* max) { int _min = COLOR_8BIT, _max = 0; if(_imageIn) { switch(_imageIn.GetBPP()) { case BPP_8: { for(int x=0;x<_imageInHeight;x++) for(int y=0;y<_imageInWidth;y++) { BYTE* p = (BYTE*)_imageIn.GetPixelAddress(x,y); int value = *p; _min = min_val(value, _min); _max = max_val(value, _max); } *min = _min; *max = _max; } break; } } return 0; } //정수 Vector 클래스를 이용한 historgram의 최소 최대값 구하는 함수 int imageProcess::SIMD_getMinMax(int *min, int *max) { BYTE minOut = COLOR_8BIT-1, maxOut = 0; if(_imageIn) { switch(_imageIn.GetBPP()) { case BPP_8: { Iu8vec16 *pImage; Iu8vec16 max_value; Iu8vec16 min_value; BYTE* p = (BYTE*)_imageIn.GetPixelAddress(0, 0); for(int x=0; x<((_imageInHeight-1)*(_imageInWidth)); x+=16) { pImage = (Iu8vec16*)(p-x); //heap 메모리에 데이터가 할당되기때문에.. 감소시키면서 읽는다. max_value = simd_max(max_value, *pImage); min_value = simd_min(min_value, *pImage); } BYTE _min[16] = {0}; BYTE _max[16] = {0}; Is8vec16* pMin = (Is8vec16*)_min; *pMin = min_value; Is8vec16* pMax = (Is8vec16*)_max; *pMax = max_value; for(int index = 0; index<sizeof(_min); index++) { minOut = min_val(_min[index], minOut); maxOut = max_val(_max[index], maxOut); } *min = (int)minOut; *max = (int)maxOut; } break; } } return 0; }위 함수에서 영상의 최소 최대값을 구하기 위해서 정수형 Vector 클래스에서 지원하는 simd_min()과 simd_max()함수를 사용하고 있습니다.
//일반적인 SISD형태의 histogram stretching 함수 int imageProcess::historgramStretching() { if(_imageIn) { int minThreshold = 0, maxThreshold = COLOR_8BIT-1; switch(_imageIn.GetBPP()) { case BPP_8: { //필요한값은 min과 max이므로 histogram으로 얻을 필요는 없음. getMinMax(&minThreshold, &maxThreshold); for(int y=0;y<_imageInHeight;y++) for(int x=0;x<_imageInWidth;x++) { BYTE* p = (BYTE*)_imageIn.GetPixelAddress(x,y); *p = (BYTE)((double)((int)*p-minThreshold)/(maxThreshold-minThreshold)*255); } } break; } } return 0; } //Vector 클래스를 이용하여 이미지의 histogram stretching을 연산하는 함수 int imageProcess::SIMD_historgramStretching() { if(_imageIn) { int minThreshold = 0, maxThreshold = COLOR_8BIT-1; switch(_imageIn.GetBPP()) { case BPP_8: { SIMD_getMinMax(&minThreshold, &maxThreshold); Iu8vec16 *pImage; BYTE tmpImage[16]; memset(tmpImage, 0, sizeof(BYTE)*16); Iu8vec16* ptmpImage = (Iu8vec16*)tmpImage; BYTE diff = maxThreshold-minThreshold; BYTE* p = (BYTE*)_imageIn.GetPixelAddress(0, 0); for(int x = 0; x<(_imageInHeight*(_imageInWidth-1)); x+=16) { pImage = (Iu8vec16*)(p-x); //heap 메모리에 데이터가 할당되기때문에.. 감소시키면서 읽는다. *ptmpImage = *pImage; for(int i=0;i<16; i++) tmpImage[i] = (tmpImage[i]-minThreshold)*255/diff; *pImage = *ptmpImage; } } break; } } return 0; }위 함수에서는 정수형 Vector 클래스중 Iu8vec16, 즉 8bit의 크기를 가지는 16개 데이터를 하나의 package로 묶어서 연산시키는 작업만 하였습니다.
최종적으로 SISD형태로 구현했을때 처리시간(Intel Core2Duo 3.0G)은 약 54ms가 나왔고, Vector 클래스를 사용하여 처리했을경우는 약 8ms가 나왔습니다. 약 7배의 성능 효과가 있었습니다.
정수형 Vector 클래스를 사용하기 위해서는 dvec.h 헤더파일을 include해줘야 합니다. 사용한 컴파일러는 Intel C++ Compiler 11.1 Update7 최신버젼입니다. 정수형 Vector 클래스는 나누기 연산을 지원하지 않다는 것도 주의해야 합니다. 처리해야할 데이터량이 크고 많을때 이 병렬처리의 효과가 극대화될 것 같다는 생각이 듭니다.
'Robotics > Software Tech.' 카테고리의 다른 글
C/C++/MFC 구글 TTS 연동 (4) | 2011.03.22 |
---|---|
MySQL 5.5.9 와 MFC 연동 (7) | 2011.03.01 |
Intel IPP(Integrated Performance Primitives)의 성능 향상정도 (0) | 2010.10.03 |
Image Processing용 Sample image (0) | 2010.09.08 |
Open-source 2D Mobile Robot Simulation (0) | 2010.06.10 |