境下配置Google Test、TBB、OpenMP和OpenCV

Linux環境下配置Google Test、TBB、OpenMP和OpenCV

最近逐漸把開發環境從Windows轉到Linux下,原因是Visual Studio提供的環境太龐大,總感覺看不到全貌,並且對於C++11的支持實在是太慢了。而在Linux下,有非常大的選擇空間,編輯器可以選vim或者emacs,兩者都是頂級的文本編輯器(不僅僅是文本編輯器)。編譯器可以選g++或者clang,兩者對於C++11的支持已經幾乎完整了。另外還有各種優秀的工具可以選擇,可以用autotools或者cmake來build工程,用gdb進行調試,用cppcheck做靜態類型檢查(也可以配置到VS中),用valgrind對程序進行動態分析。這就是Linux的好處,各種優秀的工具隨你組合,VS是個巨無霸,感覺程序員的創造力受到了限制。

這兩天把VS上的工程全部移植到Linux上,全部用g++配合makefile進行編譯,因爲代碼規模不是特別大,所以makefile是直接手寫的。移植的過程中,一些第三方的庫要配置,包括Goolge Test(Google的C++測試框架)、TBB(Intel的C++多線程庫)、OpenMP(開放標準的並行程序指導性註釋)和OpenCV(一個跨平臺的計算機視覺庫)。所以把配置的過程記錄下來,方便以後查閱,也希望能夠給大家提供一些參考。

一、配置Google Test

現在gtest的最新版本是1.6.0,按以下步驟下載和安裝:

wget https://googletest.googlecode.com/files/gtest-1.6.0.zip
unzip gtest-1.6.0.zip 
cd gtest-1.6.0
g++ -I./include -I./ -c ./src/gtest-all.cc 
ar -rv libgtest.a gtest-all.o

注意:這邊其實就是產生了libgtest.a文件,以後需要用的時候,就把這個靜態庫拷貝到對應的工程下,鏈接的時候加上它就可以了,如:

g++ –o target source1.o source2.o libgtest.a

另外,把gtest-1.6.0下面的include/gtest目錄拷貝到全局頭文件目錄,如:

cp -r include/gtest/ /usr/local/include/

在用到gtest的文件中,用#include <gtest/gtest.h>指令就可以讓編譯器找到gtest的頭文件了。

比如我寫了一個比較幾個常用排序的算法的測試:

複製代碼
#include <gtest/gtest.h>    // 引入gtest頭文件

#include "QuickSort.h"
#include "InsertionSort.h"
#include "HeapSort.h"

using namespace CodeMood;

TEST(SortingTest, insertion_sort)    // 用TEST宏定義一個測試用例,括號裏的兩個參數起標識作用
{
    vector<int> vec = generate_random();    
    TIME_STD(test_sorting(insertion_sort, vec));
    EXPECT_TRUE(is_sorted(begin(vec), end(vec)));    // 待驗證的性質
}

TEST(SortingTest, heap_sort)
{
    vector<int> vec = generate_random(1);
    TIME_STD(test_sorting(heap_sort, vec));
    EXPECT_TRUE(is_sorted(begin(vec), end(vec)));
}

TEST(SortingTest, quick_sort)
{
    vector<int> vec = generate_random();    
    TIME_STD(test_sorting(quick_sort, vec));
    EXPECT_TRUE(is_sorted(begin(vec), end(vec)));
}

TEST(SortingTest, std_sort)
{
    vector<int> vec = generate_random();    
    TIME_STD(std::sort(begin(vec), end(vec)));
    EXPECT_TRUE(is_sorted(begin(vec), end(vec)));
}

int main(int argc, char* argv[])
{
    ::testing::InitGoogleTest(&argc, argv);    // 初始化gtest
    return RUN_ALL_TESTS();                    // 運行所有測試用例
}
複製代碼

用法很簡單,從上面的例子應該就知道怎麼用了,這邊不具體說明用法,有興趣的自己Google。運行結果如下:

image

其中TIME_STD是我自定義的一個記錄函數運行時間的一個宏,gtest本身也是帶時間統計的,總體來說兩者時間還是差不多的。

 

二、配置TBB

TBB最新版本是4.1,按以下步驟下載安裝:

wget http://threadingbuildingblocks.org/sites/default/files/software_releases/source/tbb41_20130314oss_src.tgz
mkdir -p /opt/intel
cd /opt/intel
tar zxvf ~/tbb41_20130314oss_src.tgz
cd tbb41_20130314oss
gmake

上面的操作之所以放在/opt下面,是因爲想把TBB裝在/opt/intel目錄下,然後用環境變量的方式讓編譯器找到這個位置,這是TBB推薦的做法。

以上這些步驟完成之後,會在tbb41_20130314oss目錄下產生build目錄,裏面是編譯出來的結果,然後:

cd build

看到裏面有兩個目錄:linux_ia32_gcc_cc4.8.1_libc2.12_kernel2.6.32_debug和linux_ia32_gcc_cc4.8.1_libc2.12_kernel2.6.32_release,分別是debug和release版本。

如何讓編譯器找到這個位置呢?推薦的做法是在~/.bashrc中添加這麼幾行:

如果使用release版本:

source /opt/intel/tbb41_20130314oss/build/linux_ia32_gcc_cc4.8.1_libc2.12_kernel2.6.32_release/tbbvars.sh
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

如果使用debug版本:

source /opt/intel/tbb41_20130314oss/build/linux_ia32_gcc_cc4.8.1_libc2.12_kernel2.6.32_debug/tbbvars.sh
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

關鍵在於tbbvars.sh,裏面其實就是設置環境變量的過程,包括CPATH、LIBRARY_PATH和LD_LIBRARY_PATH,但是這個腳本直接將這三個環境變量設置爲tbb的編譯目錄,而不是添加到當前的環境變量之後,所以需要注意一下。

在使用的時候,加上必要的頭文件#include <tbb/xxxx.h>,並且需要通過-ltbb選項進行鏈接。下面是我用TBB裏面的parallel_sort進行排序的的代碼:

複製代碼
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <chrono>
#include <iomanip>
#include <tbb/task_scheduler_init.h>
#include <tbb/parallel_sort.h>

using namespace std;

const int SIZE = 10000000;

#define TIME_STD(X) { \
    auto t0 = chrono::high_resolution_clock::now(); \
    {X;} \
    auto t1 = chrono::high_resolution_clock::now(); \
    cout << setw(10) << fixed << (double)chrono::duration_cast<chrono::nanoseconds>(t1-t0).count() / (double)1000000000 << "ms " << #X << endl; \
}

int main(int argc, char* argv[])
{
    vector<int> vec_int(SIZE);
    iota(begin(vec_int), end(vec_int), 0);
    srand(0);
    random_shuffle(begin(vec_int), end(vec_int));
    
    //TIME_STD(sort(begin(vec_int), end(vec_int)));
    TIME_STD(tbb::task_scheduler_init _; tbb::parallel_sort(begin(vec_int), end(vec_int)));
    assert(is_sorted(begin(vec_int), end(vec_int)));

    return 0;
}
複製代碼

makefile是這樣的(本文中其它地方的例子的makefile和這個大體類似,所以只在這個地方貼出來):

複製代碼
OBJS = ParallelSort.o
CPPFLAGS = -Wall -std=c++11 -O2
LDFLAGS = -ltbb
ParallelSort: ${OBJS} 
    g++ ${LDFLAGS} -o $@ ${OBJS}
ParallelSort.o: ParallelSort.cpp
    g++ ${CPPFLAGS} -c ParallelSort.cpp -o $@
clean:
    rm -f ParallelSort ${OBJS}
複製代碼

parallel_sort的效率顯然比std::sort高,根據核心數的多少略有不同,大家可以自己試一試。

 

三、配置OpenMP

OpenMP其實並不需要配置,多數C++編譯器都是內在支持了,要注意的是,如果程序使用OpenMP指令,在源程序裏面要加上#include <omp.h>,編譯和鏈接的時候要加上-fopenmp選項,否則會有警告甚至是錯誤,比如:undefined reference to `omp_get_num_threads'。

 

四、配置OpenCV

OpenCV的最新版本爲2.4.5,下載地址:http://superb-dca3.dl.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.5/opencv-2.4.5.tar.gz

安裝方式主要參考:http://docs.opencv.org/doc/tutorials/introduction/linux_install/linux_install.html

因爲OpenCV是圖形庫(視覺庫),所以依賴於很多包,包括:

GCC 4.4.x or later;

CMake 2.6 or higher,cmake相當於autotools,但是易用性和友好性更佳;

GTK+2.x or higher;

Git(如果用git方式下載源碼的話);

pkgconfig;

Python 2.6 or later;

ffmpeg;

還有可選的libjpeg,libpng,libtiff等。

依賴包都裝好了以後:

複製代碼
wget http://superb-dca3.dl.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.5/opencv-2.4.5.tar.gz
tar zxvf opencv-2.4.5.tar.gz
cd opencv-2.4.5
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
make install
複製代碼

這樣opencv就編譯安裝好了。

要注意的是要確保LD_LIBRARY_PATH中包含了/usr/local/lib,這樣編譯器才能連接到動態庫。編譯鏈接的時候,還需要加上這樣的選項:

編譯時:`pkg-config opencv --cflags opencv`

鏈接時:`pkg-config opencv --libs opencv`

如果編譯和鏈接放在一起:`pkg-config opencv --libs --cflags opencv`。

貼出一段很有意思的代碼:

複製代碼
#include <omp.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/legacy/legacy.hpp>

using namespace std;

#define NUMBER 100  
#define DELAY 5  
char wndname[] = "Drawing Demo";  
  
CvScalar random_color(CvRNG* rng)  
{  
    int icolor = cvRandInt(rng);  
    return CV_RGB(icolor&255, (icolor>>8)&255, (icolor>>16)&255);  
}  

int test()
{
    int line_type = CV_AA; // change it to 8 to see non-antialiased graphics  
    int i;  
    CvPoint pt1,pt2;  
    double angle;  
    CvSize sz;  
    CvPoint  ptt[6];  
    CvPoint* pt[2];  
    int  arr[2];  
    CvFont font;  
    CvRNG rng;  
    int width = 1000, height = 700;  
    int width3 = width*3, height3 = height*3;  
    CvSize text_size;  
    int ymin = 0;  
    // Load the source image  
    IplImage* image = cvCreateImage( cvSize(width,height), 8, 3 );  
    IplImage* image2;  
  
    // Create a window  
    cvNamedWindow(wndname, 1 );  
    cvZero( image );  
    cvShowImage(wndname,image);  
    cvWaitKey(DELAY);  
  
    rng = cvRNG((unsigned)-1);  
    pt[0] = &(ptt[0]);  
    pt[1] = &(ptt[3]);  
  
    arr[0] = 3;  
    arr[1] = 3;  
  
    for (i = 0; i< NUMBER; i++)  
    {  
        pt1.x=cvRandInt(&rng) % width3 - width;  
        pt1.y=cvRandInt(&rng) % height3 - height;  
        pt2.x=cvRandInt(&rng) % width3 - width;  
        pt2.y=cvRandInt(&rng) % height3 - height;  
  
        cvLine( image, pt1, pt2, random_color(&rng), cvRandInt(&rng)%10, line_type, 0 );  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    for (i = 0; i< NUMBER; i++)  
    {  
        pt1.x=cvRandInt(&rng) % width3 - width;  
        pt1.y=cvRandInt(&rng) % height3 - height;  
        pt2.x=cvRandInt(&rng) % width3 - width;  
        pt2.y=cvRandInt(&rng) % height3 - height;  
  
        cvRectangle( image,pt1, pt2, random_color(&rng), cvRandInt(&rng)%10-1, line_type, 0 );  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    for (i = 0; i< NUMBER; i++)  
    {  
        pt1.x=cvRandInt(&rng) % width3 - width;  
        pt1.y=cvRandInt(&rng) % height3 - height;  
        sz.width =cvRandInt(&rng)%200;  
        sz.height=cvRandInt(&rng)%200;  
        angle = (cvRandInt(&rng)%1000)*0.180;  
  
        cvEllipse( image, pt1, sz, angle, angle - 100, angle + 200,  
                   random_color(&rng), cvRandInt(&rng)%10-1, line_type, 0 );  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    for (i = 0; i< NUMBER; i++)  
    {  
        pt[0][0].x=cvRandInt(&rng) % width3 - width;  
        pt[0][0].y=cvRandInt(&rng) % height3 - height;  
        pt[0][1].x=cvRandInt(&rng) % width3 - width;  
        pt[0][1].y=cvRandInt(&rng) % height3 - height;  
        pt[0][2].x=cvRandInt(&rng) % width3 - width;  
        pt[0][2].y=cvRandInt(&rng) % height3 - height;  
        pt[1][0].x=cvRandInt(&rng) % width3 - width;  
        pt[1][0].y=cvRandInt(&rng) % height3 - height;  
        pt[1][1].x=cvRandInt(&rng) % width3 - width;  
        pt[1][1].y=cvRandInt(&rng) % height3 - height;  
        pt[1][2].x=cvRandInt(&rng) % width3 - width;  
        pt[1][2].y=cvRandInt(&rng) % height3 - height;  
  
        cvPolyLine( image, pt, arr, 2, 1, random_color(&rng), cvRandInt(&rng)%10, line_type, 0 );  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    for (i = 0; i< NUMBER; i++)  
    {  
        pt[0][0].x=cvRandInt(&rng) % width3 - width;  
        pt[0][0].y=cvRandInt(&rng) % height3 - height;  
        pt[0][1].x=cvRandInt(&rng) % width3 - width;  
        pt[0][1].y=cvRandInt(&rng) % height3 - height;  
        pt[0][2].x=cvRandInt(&rng) % width3 - width;  
        pt[0][2].y=cvRandInt(&rng) % height3 - height;  
        pt[1][0].x=cvRandInt(&rng) % width3 - width;  
        pt[1][0].y=cvRandInt(&rng) % height3 - height;  
        pt[1][1].x=cvRandInt(&rng) % width3 - width;  
        pt[1][1].y=cvRandInt(&rng) % height3 - height;  
        pt[1][2].x=cvRandInt(&rng) % width3 - width;  
        pt[1][2].y=cvRandInt(&rng) % height3 - height;  
  
        cvFillPoly( image, pt, arr, 2, random_color(&rng), line_type, 0 );  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    for (i = 0; i< NUMBER; i++)  
    {  
        pt1.x=cvRandInt(&rng) % width3 - width;  
        pt1.y=cvRandInt(&rng) % height3 - height;  
  
        cvCircle( image, pt1, cvRandInt(&rng)%300, random_color(&rng),  
                  cvRandInt(&rng)%10-1, line_type, 0 );  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    for (i = 1; i< NUMBER; i++)  
    {  
        pt1.x=cvRandInt(&rng) % width3 - width;  
        pt1.y=cvRandInt(&rng) % height3 - height;  
  
        cvInitFont( &font, cvRandInt(&rng) % 8,  
                    (cvRandInt(&rng)%100)*0.05+0.1, (cvRandInt(&rng)%100)*0.05+0.1,  
                    (cvRandInt(&rng)%5)*0.1, cvRound(cvRandInt(&rng)%10), line_type );  
  
        cvPutText( image, "Testing text rendering!", pt1, &font, random_color(&rng));  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    cvInitFont( &font, CV_FONT_HERSHEY_COMPLEX, 3, 3, 0.0, 5, line_type );  
  
    cvGetTextSize( "OpenCV forever!", &font, &text_size, &ymin );  
  
    pt1.x = (width - text_size.width)/2;  
    pt1.y = (height + text_size.height)/2;  
    image2 = cvCloneImage(image);  
  
    for( i = 0; i < 255; i++ )  
    {  
        cvSubS( image2, cvScalarAll(i), image, 0 );  
        cvPutText( image, "OpenCV forever!", pt1, &font, CV_RGB(255,i,i));  
        cvShowImage(wndname,image);  
        if(cvWaitKey(DELAY) >= 0) return 0;  
    }  
  
    // Wait for a key stroke; the same function arranges events processing  
    cvWaitKey(0);  
    cvReleaseImage(&image);  
    cvReleaseImage(&image2);  
    cvDestroyWindow(wndname);   
    return 0;
}

int main(int argc, char* argv[])
{
    test();
    return 0;
}
複製代碼

運行效果:

image

image

怎麼樣,很酷吧?

OK,四個環境的配置就寫完了!吃飯去~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章