Debian Jessie + GCC 4.8 + NVIDIA CUDA 6.5

虽然Debian Jessie的官方库已经提供nvidia-cuda-toolkit这个包。但是,作为为东家赚钱的工具,开源社区很难得到东家的真正支持。目前这个包的版本是6.0,而Nvidia官方提供的CUDA版本已经到了7.0。另外一方面,Nvidia的Tegra K1芯片的开发板Jetson TK-1上仍然在运行6.5版的CUDA。如果你也和我一样希望在移动平台上做并行计算,那么CUDA 6.5是在百般纠结之后的唯一正确选择。

Debian Jessie已经将默认的GCC版本升级到gcc-4.9;而CUDA 6.5的编译器nvcc却只能与gcc-4.8协同工作。所以,又遇到比上一个状况更纠结的问题。

这篇文章就是记录下如何解决这些问题的方法。

第一,从NVIDIA官方下载.run的自解压文件安装CUDA 6.5。下载页面在这里:

https://developer.nvidia.com/cuda-downloads-geforce-gtx9xx

,或者直接使用下面的命令下载

# wget http://developer.download.nvidia.com/compute/cuda/6_5/rel/installers/cuda_6.5.19_linux_64.run -O /tmp/cuda_6.5.19_linux_64.run

下载完成后,运行安装程序:

# chmod +x /tmp/cuda_6.5.19_linux_64.run
# /tmp/cuda_6.5.19_linux_64.run

这个过程中安装程序会提醒系统的gcc版本不能与当前CUDA工具链一起工作,并且中止安装过程。

You are attempting to install on an unsupported configuration. Do you wish to continue? ((y)es/(n)o) [ default is no ]: 

此时只能回答“no”并且退出安装。只有检查安装LOG才能发现NVIDIA的小秘密!那就是,

使用这个参数重新运行安装程序

# /tmp/cuda_6.5.19_linux_64.run --override

安装程序还会询问安装位置,为了避免和debian的包管理冲突,我选择了/opt/nvidia/cuda-6.5。同时,安装程序还会将CUDA的样例代码安装到${HOME}

第二,在Debian Jessie上安装gcc-4.8g++-4.8并在每次编译CUDA代码是将其路径指定给编译程序。步骤如下:

# sudo apt-get install gcc-4.8 g++-4.8
# cd ${HOME}/NVIDIA_CUDA-6.5_Samples/
# GCC=g++-4.8 make

用下面的命令运行最简单的样例代码确认结果。

# ${HOME}/NVIDIA_CUDA-6.5_Samples/bin/x86_64/linux/release/simpleTexture

在OpenCV中使用视频

目前Computer Vision领域最前沿的技术都集中在视频分析上。理所当然得,OpenCV也支持USB摄像头作为一个重要的输入类别。此前,OpenCV 1.1的API就提供了cvCaptureFromCAM函数从摄像头取得数据。OpenCV 2.0开始视频输入被包装成了VideoCapture这个类。而且,使用上也更加简便。虽然看上去在OpenCV中使用从摄像头作为输入并不困难,但是因为缺乏广泛的厂商支持,这件事情在Linux操作系统上却常常让人担心。这篇文章的主旨还是将我目前的配置记录下来,以便应付我那不怎么可靠的记忆力。

硬件

虽然Linux的世界Microsoft永远不会懂,但是Linux的开发者却一直在试图弄懂Microsoft的硬件,而且结果相当不错。Microsoft的硬件LifeCam几乎可以在Debian Jessie上面完美运行!这是我使用的LifeCam Cimema(官网盗图):

Microsoft LifeCam Cinema

v4l2驱动

V4L是Linux内核为各种视频输入设备提供的驱动框架。为了使用USB接口的视频设备,在系统中还需要其他内核模块支持这些硬件。以下是必要的内核模块列表:

$ lsmod | grep v4l
v4l2_common            12995  1 videobuf2_core
videodev              126451  4 uvcvideo,v4l2_common,videobuf2_core
i2c_core               46012  5 drm,i2c_i801,nvidia,v4l2_common,videodev



除此以外,用户空间还需要像gstreamer这样的视频处理框架来支持视频的编解码和显示。如果一一安装这些软件组件,那将会是一个非常痛苦的过程。所以,为了方便起见,直接安装处于这个软件栈的最顶端的应用程序将会达到事半功倍的效果。安装下面的程序:

$ sudo apt-get install v4l2ucp guvcview



安装结束后,分层确认安装结果。使用v4l2ctrl确认视频设备的配置。

$ v4l2ctrl -s /tmp/result



这个程序会探测系统中的/dev/video0并且将其配置保存到/tmp/result。在我的LifeCam上的确认结果如下:

9963776:                     Brightness:133
9963777:                       Contrast:5
9963778:                     Saturation:83
9963788:White Balance Temperature, Auto:1
9963800:           Power Line Frequency:2
9963802:      White Balance Temperature:4500
9963803:                      Sharpness:25
9963804:         Backlight Compensation:0
10094849:                 Exposure, Auto:3
10094850:            Exposure (Absolute):156
10094856:                 Pan (Absolute):0
10094857:                Tilt (Absolute):0
10094858:               Focus (absolute):4
10094860:                    Focus, Auto:1
10094861:                 Zoom, Absolute:0



然后,使用guvcview这个应用程序来测试视频录像。

guvcview_video_capture

使用OpenCV抓取视频

#include <opencv2/opencv.hpp>

using namespace cv;

int main(int argc, char** argv)
{
  VideoCapture cap(0); // open the default camera
  if (!cap.isOpened()) // check if we succeeded
    return -1;

  Mat edges;
  namedWindow("Edges", 1);

  for (;;) {
    Mat frame;
    cap >> frame; // get a new frame from camera
    cvtColor(frame, edges, CV_BGR2GRAY);
    GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
    Canny(edges, edges, 0, 30, 3);
    imshow("Edges", edges);
    if (waitKey(30) >= 0) break;
  }

  // the camera will be deinitialized automatically in VideoCapture
  // destructor
  return 0;
}



这段程序可以从摄像头抓取视频,并且对视频中的帧进行边缘探测。主循环使用了Canny边缘探测法。下面是运行时的截图。

OpenCv_VideoCapture_Example

在Debian Jessie上进行OpenCV编程

Debian Jessie对OpenCV的运行环境和开发库都进行了较大的改动。例如,libcv-dev包已经被重命名为libopencv-dev等。此外,相关动态链接库都使用libopencv_作为前缀进行命名。在链接是使用-lcv这样的方法已经不再适用了。所以,这篇文章是为了将如何编译和运行OpenCV程序的方法记录下来。

安装OpenCV开发环境

sudo apt-get install libopencv-dev

查看安装后的库和头文件

$ find /usr/lib -name "*opencv*"
...
/usr/lib/x86_64-linux-gnu/libopencv_core.so
...

$ find /usr/include -name "*opencv*"
/usr/include/opencv
/usr/include/opencv2
/usr/include/opencv2/opencv_modules.hpp
/usr/include/opencv2/opencv.hpp

Hello OpenCV

////////////////////////////////////////////////////////////////////////
//
// hello-opencv.cpp
//
// This is a simple, introductory OpenCV program. The program reads an
// image from a file, inverts it, and displays the result.
//
////////////////////////////////////////////////////////////////////////
#include <opencv/cv.h>
#include <opencv/highgui.h>

int main(int argc, char *argv[])
{
  IplImage* img = 0;
  int height, width, step, channels;
  uchar *data;
  int i, j, k;

  if (argc < 2) {
    printf("Usage: main \n\7");
    exit(0);
  }

  // load an image
  img = cvLoadImage(argv[1]);
  if (!img) {
    printf("Could not load image file: %s\n",argv[1]);
    exit(0);
  }

  // get the image data
  height    = img->height;
  width     = img->width;
  step      = img->widthStep;
  channels  = img->nChannels;
  data      = (uchar *)img->imageData;
  printf("Processing a %dx%d image with %d channels\n",
         height, width, channels);

  // create a window
  cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
  cvMoveWindow("mainWin", 100, 100);

  // invert the image
  for (i = 0; i < height; i++)
    for (j = 0; j < width; j++)
      for(k = 0; k < channels; k++)
        data[i * step + j * channels + k] =
                255 - data[i * step + j* channels + k];

  // show the image
  cvShowImage("mainWin", img );

  // wait for a key
  cvWaitKey(0);

  // release the image
  cvReleaseImage(&img);
  return 0;
}

编译

$ g++ -o /tmp/hello-opencv hello-opencv.cpp -lopencv_core -lopencv_highgui

运行

$ /tmp/hello-opencv /tmp/test.jpg

这个程序会创建一个窗口并且将输入的图像转换成反色显示。