本文主要内容:
在Raspberry Pi上,通过调用OpenCV库,自己写一个简单的C程序来控制摄像头拍照,并保存图片。
在阅读本文之前,请确保你已经阅读过我的另一篇配置Pi的文章,因为本文是在它的基础上进行的。
The main content of this article:
Write a simple C program by invoking the OpenCV library on Raspberry Pi to control a webcam to take photos & save them.
Before reading this article, please make sure that you've read my another article about Pi configuration, because this article is based on that.
图:我的Pi正在运行中 / my running Raspberry Pi
【1】安装OpenCV / Install OpenCV
命令:
Command:
1 | pacman -S opencv opencv-samples |
上面的命令连OpenCV的sample一起装上了,这样我们在安装好之后就可以编译几个sample来测试。
This command will install the samples of OpenCV, too. So we can compile & run several samples to have a test after the installation.
【2】带动摄像头 / Drive the webcam
在驱动已经可用的情况下,把一个USB摄像头接在USB口上也不一定能让它工作,因为Pi的USB口的电流供应可能不足。请看这个链接的玩家自制的一根增强供电的USB线,如果你想简单安全,那么你可以把摄像头接在一个有外部供电的USB HUB上,再把Pi的USB接到USB HUB上,这样摄像头的供电就充足了。
Plug the USB webcam into the USB interface of Pi might not able to make it work, even the driver has been available, because the current provided by Pi's USB interface may not be sufficient to drive a camera. You can learn to DIY a current-enhancement USB cable via this link, but the easier way may be buying a powered USB HUB and use it to connect the webcam & Pi.
【3】让opencv的sample跑起来 / Run a sample of OpenCV
为了验证在Pi上安装的OpenCV是可用的,所以至少要让一个sample可以跑起来。
为了不把安装目录弄得一团乱,首先把安装好的OpenCV的sample目录拷贝到其他地方:
To make sure that the installed OpenCV related packages are "correct", I decided to run at least one of the OpenCV samples.
Copy the installed OpenCV sample directory to other place for the sake of not messing up the installation directory:
1 2 | mkdir/root/opencv cp-r/usr/share/OpenCV/samples//root/opencv/ |
我们看到里面有“c”,“cpp”和“gpu”三种sample,选最简单的,我们进入“c”,先为所有文件加上写权限:
There are 3 kinds of samples in it, "c", "cpp" & "gpu", so I choose the simplest - "c", and enter the "c" directory, add write permission to all files:
1 | chmod+w * |
然后我们看到“c”目录里有编译脚本 build_all.sh,执行它,看到出了一堆相似的错误,没有一个程序可以成功编译,例如以下错误:
Execute the build_all.sh script to compile an sample, then I got lots of similar errors, e.g. :
[root@alarmpi c]# ./build_all.sh contours.c compiling contours /usr/bin/ld: /tmp/cch4zRSn.o: undefined reference to symbol 'lrint@@GLIBC_2.4' /usr/bin/ld: note: 'lrint@@GLIBC_2.4' is defined in DSO /usr/lib/libm.so.6 so try adding it to the linker command line /usr/lib/libm.so.6: could not read symbols: Invalid operation collect2: error: ld returned 1 exit status由 libm 可见,这是因为数学库没有被包含在编译命令中,于是我们编译 build_all.sh,为所有编译命令添加上 -lm 编译参数:
The "libm" told me that the math library was not included in the compiling command, so I edited build_all.sh and add -lm to all compiling command:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | #!/bin/sh if[ $# -gt 0 ] ; then base=`basename$1 .c` echo"compiling $base" gcc -ggdb `pkg-config opencv --cflags --libs` $base.c -o $base -lm else foriin*.c;do echo"compiling $i" gcc -ggdb `pkg-config --cflags opencv` -o `basename$i .c` $i `pkg-config --libs opencv` -lm; done foriin*.cpp;do echo"compiling $i" g++ -ggdb `pkg-config --cflags opencv` -o `basename$i .cpp` $i `pkg-config --libs opencv` -lm; done fi |
还忘了说一句,由于上面的脚本在编译时使用了pkg-config,所以在编译前,我们还需要先安装 pkg-config:
Sorry, I forget to mention that because of using pkg-config, we need to install pkg-config before compiling the sample:
1 | pacman -S pkg-config |
然后再编译,成功。
Then the compiling will succeed.
那么下面,我们再接着尝试把编译出的sample运行起来:
Then run the executable program we just compiled:
01 02 03 04 05 06 07 08 09 10 11 | [root@alarmpi c]# ./contours This program creates an image to demonstrate the use of the "c" contour functions: cvFindContours() and cvApproxPoly() along with the storage functions cvCreateMemStorage() and cvDrawContours(). It also shows the use of a trackbar to control contour retrieval. Usage : ./contours (image:460): Gtk-WARNING **: cannot open display: |
很明显,运行不起来。
看样子是跟gtk相关,并且我在网上搜到这跟X server什么的可能有关系,于是安装gtk和X server:
It failed, unfortunately.
And Google told me that the error is related to X server, too, so I installed gtk & X server:
1 | pacman -S gtk xorg-server |
再修改配置文件 /etc/ssh/sshd_config :
Modify the config file /etc/ssh/sshd_config :
1 2 3 4 5 6 7 8 | #AllowTcpForwarding yes AllowTcpForwarding yes #GatewayPorts no #X11Forwarding no X11Forwarding yes #X11DisplayOffset 10 #X11UseLocalhost yes X11UseLocalhost yes |
注意,没有注释的那三行是我添加的。
Note that the 3 uncommented lines are added by me.
重启Pi的系统,运行X server:
Reboot Pi & run X server:
1 | X |
没错,命令就是这么简单。然后又出错了:
Yes, the command is just so simple, but it failed to start:
[root@alarmpi c]# X X.Org X Server 1.13.0 Release Date: 2012-09-05 X Protocol Version 11, Revision 0 Build Operating System: Linux 3.0.15-1-ARCH+ armv6l Current Operating System: Linux alarmpi 3.2.27-11-ARCH+ #1 PREEMPT Sat Oct 13 22:46:16 UTC 2012 armv6l Kernel command line: dma.dmachans=0x7f35 bcm2708_fb.fbwidth=1920 bcm2708_fb.fbheight=1080 bcm2708.boardrev=0x3 bcm2708.serial=0x398ca41f smsc95xx.macaddr=B8:27:EB:8C:A4:1F vc_mem.mem_base=0xc000000 vc_mem.mem_size=0x10000000 sdhci-bcm2708.enable_llm=1 sdhci-bcm2708.sync_after_dma=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 loglevel=2 root=/dev/mmcblk0p2 init=/bin/systemd rootfstype=ext4 rootwait Build Date: 14 October 2012 10:05:22PM Current version of pixman: 0.26.2 Before reporting problems, check http://wiki.x.org to make sure that you have the latest version. Markers: (--) probed, (**) from config file, (==) default setting, (++) from command line, (!!) notice, (II) informational, (WW) warning, (EE) error, (NI) not implemented, (??) unknown. (==) Log file: "/var/log/Xorg.0.log", Time: Mon Oct 15 14:30:03 2012 (==) Using config directory: "/etc/X11/xorg.conf.d" Initializing built-in extension Generic Event Extension Initializing built-in extension SHAPE Initializing built-in extension MIT-SHM Initializing built-in extension XInputExtension Initializing built-in extension XTEST Initializing built-in extension BIG-REQUESTS Initializing built-in extension SYNC Initializing built-in extension XKEYBOARD Initializing built-in extension XC-MISC Initializing built-in extension SECURITY Initializing built-in extension XINERAMA Initializing built-in extension XFIXES Initializing built-in extension RENDER Initializing built-in extension RANDR Initializing built-in extension COMPOSITE Initializing built-in extension DAMAGE Initializing built-in extension MIT-SCREEN-SAVER Initializing built-in extension DOUBLE-BUFFER Initializing built-in extension RECORD Initializing built-in extension DPMS Initializing built-in extension X-Resource Initializing built-in extension XVideo Initializing built-in extension XVideo-MotionCompensation Initializing built-in extension XFree86-VidModeExtension Initializing built-in extension XFree86-DGA Initializing built-in extension XFree86-DRI Initializing built-in extension DRI2 Loading extension GLX Fatal server error: no screens found (EE) Please consult the The X.Org Foundation support at http://wiki.x.org for help. (EE) Please also check the log file at "/var/log/Xorg.0.log" for additional information. (EE) Server terminated with error (1). Closing log file.
既然它让我看日志文件 /var/log/Xorg.0.log,那我就看。在日志文件的最后,发现提示找不到 fbdev 模块/库,于是翻遍Google,找到一个答案:应该安装 xf86-video-fbdev 包:
Since it told me to check log file /var/log/Xorg.0.log, then I did. At the last few lines of the log file, I found an error message which indicated a fbdev module/lib missing, so I ask Google again for a while, and I realized that I should install package xf86-video-fbdev:
1 | pacman -S xf86-video-fbdev |
文章来源:http://www.codelast.com/
再用“X”命令启动X server,成功了:
Then run "X" again, it succeeded:
[root@alarmpi ~]# X X.Org X Server 1.13.0 Release Date: 2012-09-05 X Protocol Version 11, Revision 0 Build Operating System: Linux 3.0.15-1-ARCH+ armv6l Current Operating System: Linux alarmpi 3.2.27-11-ARCH+ #1 PREEMPT Sat Oct 13 22:46:16 UTC 2012 armv6l Kernel command line: dma.dmachans=0x7f35 bcm2708_fb.fbwidth=1920 bcm2708_fb.fbheight=1080 bcm2708.boardrev=0x3 bcm2708.serial=0x398ca41f smsc95xx.macaddr=B8:27:EB:8C:A4:1F vc_mem.mem_base=0xc000000 vc_mem.mem_size=0x10000000 sdhci-bcm2708.enable_llm=1 sdhci-bcm2708.sync_after_dma=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 loglevel=2 root=/dev/mmcblk0p2 init=/bin/systemd rootfstype=ext4 rootwait Build Date: 14 October 2012 10:05:22PM Current version of pixman: 0.26.2 Before reporting problems, check http://wiki.x.org to make sure that you have the latest version. Markers: (--) probed, (**) from config file, (==) default setting, (++) from command line, (!!) notice, (II) informational, (WW) warning, (EE) error, (NI) not implemented, (??) unknown. (==) Log file: "/var/log/Xorg.0.log", Time: Mon Oct 15 14:32:34 2012 (==) Using config directory: "/etc/X11/xorg.conf.d" Initializing built-in extension Generic Event Extension Initializing built-in extension SHAPE Initializing built-in extension MIT-SHM Initializing built-in extension XInputExtension Initializing built-in extension XTEST Initializing built-in extension BIG-REQUESTS Initializing built-in extension SYNC Initializing built-in extension XKEYBOARD Initializing built-in extension XC-MISC Initializing built-in extension SECURITY Initializing built-in extension XINERAMA Initializing built-in extension XFIXES Initializing built-in extension RENDER Initializing built-in extension RANDR Initializing built-in extension COMPOSITE Initializing built-in extension DAMAGE Initializing built-in extension MIT-SCREEN-SAVER Initializing built-in extension DOUBLE-BUFFER Initializing built-in extension RECORD Initializing built-in extension DPMS Initializing built-in extension X-Resource Initializing built-in extension XVideo Initializing built-in extension XVideo-MotionCompensation Initializing built-in extension XFree86-VidModeExtension Initializing built-in extension XFree86-DGA Initializing built-in extension XFree86-DRI Initializing built-in extension DRI2 Loading extension GLX
再试着运行OpenCV的sample,依然不行,Google搜索到一篇在Pi上运行OpenCV的文章,作者做了这样一件事情:
Then I tried to run the OpenCV sample again, still the same error("cannot open display"), so I Googled another article about using OpenCV on Pi and found that the author did one thing:
1 | exportDISPLAY=192.168.3.65:0.0 |
其中,192.168.3.65是Pi的IP地址。这样设置之后,再运行,程序不再报错了!再一看相同的目录下,生成了一个 contours.xml 文件,说明程序执行成功了。
In which the "192.168.3.65" is your Pi's IP address. After setting this, the sample could run without any error occurred, and a contours.xml file was generated under the same directory, which indicated that we succeeded.
【4】写一个简单的OpenCV C程序 / Write a simple OpenCV C program
现在该写一个简单的OpenCV C程序来控制摄像头了。
Now we should write a simple C program to control the webcam by using OpenCV.
直接看程序:
Let's check the C code:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <stdio.h> #include <stdlib.h> #include "highgui.h" #include "opencv.hpp" intmain(intargc,char**argv) { /* init camera */ CvCapture* pCapture = cvCreateCameraCapture(0); cvSetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_WIDTH, 320); cvSetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_HEIGHT, 240); //cvSetCaptureProperty(pCapture, CV_CAP_PROP_FPS, 5); IplImage *pFrame = 0; if(NULL == pCapture) { fprintf(stderr,"Can't initialize webcam!\n"); return1; } pFrame = cvQueryFrame(pCapture); // query a frame if(NULL == pFrame) { fprintf(stderr,"Can't get a frame!\n"); return1; } constchar*pImageFileName ="webcam.jpg"; cvSaveImage(pImageFileName, pFrame); cvReleaseCapture(&pCapture); // free memory return0; } |
此程序做的事情非常简单:初始化摄像头,抓取一帧,然后保存为名为“webcam.jpg”的图片。
This program does a very simple job: initialize the camera, capture a frame & save it to a image file named "webcam.jpg".
文章来源:http://www.codelast.com/
编译自己的webcam程序:
Then compile the webcam program:
1 | g++ -I/usr/include/opencv/-I/usr/include/opencv2/ `pkg-config --cflags opencv --libs opencv` webcam.c -o webcam |
会生成一个可执行程序“webcam”,运行它:
This will generate an executable bin "webcam", just run it:
1 | ./webcam |
然后在相同目录下,会看到生成了一个“webcam.jpg”图片,正是摄像头拍的照。
Then we'll get a "webcam.jpg" image file under the same directory, which is just the photo took by the camera.
这样,本文的主要目的就达成了。可以在此基础上继续开发其他的程序,来实现更为有用的功能,例如像摄像头娘一样的运动检测功能。
So the main purpose of this article has been reached. We can develop more useful programs base on it, e.g. a program similar to the Camera Girl, which implements the motion detection functionality.
文章来源:http://www.codelast.com/
【5】遗留问题 / Unsolved problems
在上述过程中,其实我还遇到了一些未解决的问题,暂时没有时间深究,在此先记录如下。
In the above process I met some unsolved problems and have no time to explore them yet, so I just write them down here for further study.
①拍照时使用稍高的分辨率就导致程序崩溃 / Use a little high resolution will cause the program crash
在上面的程序中,如下两句是用于设置分辨率的(宽*高):
In the above codes, the two lines below are used to set the resolution:
1 2 | cvSetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_WIDTH, 320); cvSetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_HEIGHT, 240); |
根据我的试验,160*120的低分辨率比较少出问题,尽管320*240的分辨率对摄像头来说也根本不算高的,但是就算是使用320*240的分辨率,程序还是偶尔会崩溃的,报错如下:
According to my experiments, the 160*120 low resolution cause less crashes than higher resolutions. Even 320*240 is not a high resolution for a webcam nowadays, it still made the program crashed some times, and the error messages are:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | [root@alarmpi c]# ./webcam VIDIOC_QUERYMENU: Invalid argument VIDIOC_QUERYMENU: Invalid argument VIDIOC_QUERYMENU: Invalid argument VIDIOC_REQBUFS: Cannot allocate memory munmap: Invalid argument munmap: Invalid argument munmap: Invalid argument munmap: Invalid argument Unable to stop the stream.: Bad file descriptor munmap: Invalid argument munmap: Invalid argument munmap: Invalid argument munmap: Invalid argument HIGHGUI ERROR: V4L: Initial Capture Error: Unable to load initial memory buffers. Can't get a frame! |
文章来源:http://www.codelast.com/
或者:
Or:
1 2 3 4 5 6 | VIDIOC_REQBUFS: Cannot allocate memory HIGHGUI ERROR: V4L/V4L2: Could not obtain specifics of capture window. Unable to stop the stream.: Bad file descriptor VIDIOC_QBUF: Bad file descriptor Can't get a frame! |
我不确定这是跟Pi的CPU计算能力较弱有关呢,还是跟Pi的RAM太小有关呢,还是跟摄像头的供电不足有关呢(我没有使用带电源的USB HUB来接摄像头),或者是跟摄像头的驱动有关呢?
如果你知道解决办法,请赐教,非常感谢。
I'm not sure whether the problem is caused by the weak computation capability of Pi's CPU, or the too-small RAM on Pi, or the insufficient current supply of the webcam(I didn't use a powered USB HUB to connect the webcam to Pi), or the webcam's Linux driver...
If you know something, please tell me and thank you in advance.