当前位置: 首页 > 工具软件 > pcDuino > 使用案例 >

pcDuino+OpenCV实现人脸追踪摄像头

谢旻
2023-12-01

OpenCV是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

pcDuino是一款兼容Arduino接口的mini pc,A8架构1Ghz的CPU,计算能力不俗,用来跑OpenCV刚刚好。这里就用他们实现一个可以跟随人脸移动的摄像头。

在优酷里面有一个视频: OpenCV+pcDuino人脸跟踪
硬件清单
1、pcDuino一块;
2、传感器扩展板一块;
3、摄像头云台一个;
4、摄像头一个.

软件环境

1、Pcduino板载Ubuntu;
2、GCC 4.6;
3、QT 4.8.5:http://qt-project.org/downloads;
4、OpenCV 2.4.7:http://opencv.org/downloads.html;
5、Arduino SDK(c_enviroment):https://github.com/pcduino/c_enviroment .

在开始动手前,我们先来捋一下思路。
通过OpenCV可以方便的实现Face Detect,OpenCV的sample目录就有这个例子。流程大致是从摄像头得到逐帧的图像,通过事先训练好的特征检测出人脸的坐标(即图像的像素坐标),最后在该帧图像上圈出人脸的位置。
我们需要的就是这个人脸的坐标,先计算出人脸坐标x轴和y轴分别偏离画面中心点的距离(单位:像素),然后根据这个偏离值驱动摄像头的云台去修正摄像头的指向,使人脸坐标与画面中心点重合(即指向人脸)。

硬件部分

硬件部分安装:

1.先在pcDuino上接驳传感器扩展板,

2.再把云台两个舵机分别接在传感器扩展板PWM 5、6针脚上(PWM与GPIO共用5、6针脚,选用5、6是因为只有5、6是硬件PWM,不会使CPU占用率太高)。

这里需要注意的一点是,如果你选用的是大功率的云台和舵机,需要为舵机独立供电。
这是安装好的图片:


软件部分

一、编译安装OpenCV:
1、先安装各种依赖库,根据你的环境不同,可能出现缺失,全都补上,以求OpenCV一次编译通过(因为编译过程耗时将近3小时)
sudo apt-get install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libjpeg62-dev libtiff4-dev cmake libswscale-dev libjasper-dev

2、下载解压OpenCV包,用cmake工具生成编译所需的信息,第四句说明编译成release版本,安装目录是/usr/local

cd ~/opencv
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..

3、开始编译
make
make install

关于OpenCV的安装大家可以参照官方文档:http://docs.opencv.org/doc/tutorials/introduction/linux_install/linux_install.html#linux-installation

二、编译安装c_enviroment:

1、c_enviroment是Pcduino控制硬件I/O的库,从开头软件环境处给的链接下载c_enviroment的zip包,解压编译
cd c_enviroment
make
编译完后,我们可以进入output/test目录,测试一下点亮led的sample

三、编译安装Qt:

为了方便开发,我这里用了Qt creator作为IDE
1、安装Qt creator
sudo apt-get install qtcreator
现在已经可以在programing里运行Qt creator了,但是这时候它还不能用,还需要安装Qt library

2、安装Qt library
通过开头软件环境中的链接下载嵌入式版Qt library:Qt libraries 4.8.5 for embedded Linux
具体安装过程大家可以参照这个帖子,这里就不赘述了:
http://www.pcduino.org/forum.php?mod=viewthread&tid=21&highlight=%E5%9C%A8pcduino%E5%AE%89%E8%A3%85Qt

经过漫长的编译安装,Qt终于完成,至此环境算是搭好一大半了。

新建一个名为face_tracking_camera的C++项目,编辑face_tracking_camera.pro文件,加入OpenCV和c_enviroment的源文件、头文件和类库路径,我这里OpenCV安装在/usr/local/,c_enviroment安装在/home/ubuntu/c_enviroment/
expand source

到这里,环境就全部搭好了。

代码

#include <opencv2/opencv.hpp>

#include <Arduino.h>

#include <wiring_private.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <assert.h>

#include <math.h>

#include <float.h>

#include <limits.h>

#include <time.h>

#include <ctype.h>

#include <sys/time.h>

#include <signal.h>

//用于人脸识别的分类器特征库文件路径

constchar* cascade_name =”/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml”;

//定义了X轴(横向摆动),Y轴(纵向摆动)舵机的中心点

staticintcenterlevelX = 110;

staticintcenterlevelY = 60;

//定义了舵机的频率

constintfrequncy = 260;

//用一个整数存储目前舵机摆动方向

staticintturningRight = 1;

//函数的签名列表

voiddetect_and_draw( IplImage* image );

voidstart_pulse(intpwm_id,intfreq,intvalue);

voidsigroutine(intdunno);

voidreset();

longgetCurrentTime();

longstartTime;

longendTime;

//led指示灯pin

intpin_led = 3;

//OpenCV申请一块用于计算的内存

staticCvMemStorage* storage = 0;

//声明一个haar分类器

staticCvHaarClassifierCascade* cascade = 0;

voidsetup(){

//定义指示灯针脚为输出

pinMode(pin_led,OUTPUT);

//复位云台舵机

reset();

//监听中断信号

signal(SIGINT,sigroutine);

//建立一个名为result的窗口

cvNamedWindow(“result”, 1 );

//打开摄像头

CvCapture* capture = cvCaptureFromCAM(-1);

//声明两个opencv图像类型

IplImage *img;

IplImage *newImg;

//加载分类器

cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );

//检查分类器加载异常

if( !cascade ){

fprintf( stderr,”ERROR: Could not load classifier cascade\n”);

}

//定位内存块

storage = cvCreateMemStorage(0);

while(1) {

//由于pcduino的计算能力有限,为了保证帧率,把从摄像头采集来的画面缩小1/2

newImg = cvQueryFrame( capture );

if( !newImg )break;

img = cvCreateImage(cvSize(newImg->width/2, newImg->height/2), newImg->depth, newImg->nChannels);

cvResize(newImg, img);

//翻转图像

cvFlip(img, img, 1);

//调用识别和绘制图像的函数

detect_and_draw(img);

//释放图像使用的内存

cvReleaseImage(&img);

//监听esc

intc = cvWaitKey(33);

if( c == 27 )break;

}

//释放摄像头

cvReleaseCapture( &capture );

//销毁窗口

cvDestroyWindow(“result”);

}

voiddetect_and_draw( IplImage* img ){

startTime = getCurrentTime();

//清空使用过的内存空间

cvClearMemStorage( storage );

intscale = 1;

inti;

//声明一个中心点存储识别出来的人脸位置

CvPoint ptcenter;

//人脸识别

if( cascade ){

//逐帧检测人脸

CvSeq* faces = cvHaarDetectObjects( img, cascade, storage,

1.1, 2, CV_HAAR_DO_CANNY_PRUNING,

cvSize(80, 80));

//如果检测到多张脸,遍历取出

for( i = 0; i < (faces ? faces->total : 0); i++ ){

//创建人脸矩形

CvRect* r = (CvRect*)cvGetSeqElem( faces, i );

//换算出人脸矩形的中心点

ptcenter.x = (r->x+(r->width/2))*scale;

ptcenter.y = (r->y+(r->height/2))*scale;

//绘制一个圆形标识出人脸的位置

cvCircle(img, ptcenter, (r->width+r->height)/4, CV_RGB(255,0,0), 3, 8, 0 );

}

}

//显示图像

cvShowImage(“result”, img );

//计算帧率

endTime = getCurrentTime();

longtime= endTime-startTime;

intframerate = 1000/time;

//检查中心点是否为空

if(ptcenter.x && ptcenter.y){

//std::cout<<”center_point:(“<<ptcenter.x<<”,”<<ptcenter.y<<”)\tframe_rate:”<<framerate<<”\n”<<std::endl;

//std::cout<<”x:”<<(ptcenter.x-img->width/2)<<”\ty:”<<(ptcenter.y-img->height/2)<<std::endl;

//led指示灯引脚输出低电平,熄灭指示灯

digitalWrite(pin_led,LOW);

//驱动摄像头移动到人脸中心位置

centerlevelX += (ptcenter.x-img->width/2)/110*2;

if(centerlevelX <= 170 && centerlevelX >= 50)start_pulse(6,frequncy,centerlevelX);

centerlevelY -= (ptcenter.y-img->height/2)/70;

if(centerlevelY <= 90 && centerlevelY >= 45)start_pulse(5,frequncy,centerlevelY);

//显示修正的XY轴幅度和帧率

std::cout<<”X:”<<centerlevelX<<”\tY:”<<centerlevelY<<”\tFrameRate:”<<framerate<<std::endl;

}else{

printf(“no face is detected in the image\n”);

//指示灯亮起

digitalWrite(pin_led,HIGH);

//如果没有检测到人脸则左右摇摆摄像头

if(centerlevelX <= 170 && turningRight == 1){

start_pulse(6,frequncy,centerlevelX+=2);

if(centerlevelX > 170)turningRight = 0;

//std::cout<<centerlevelX<<std::endl;

}

if(centerlevelX >= 50 && turningRight == 0){

start_pulse(6,frequncy,centerlevelX-=2);

if(centerlevelX < 50)turningRight =1;

//std::cout<<centerlevelX<<std::endl;

}

}

//防止摄像头下移过度

if(centerlevelY < 45) centerlevelY = 45;

}

//复位函数,调整舵机XY轴到中心位置

voidreset(){

delay(50);

start_pulse(5,frequncy,60);

start_pulse(6,frequncy,110);

delay(50);

}

longgetCurrentTime(){

structtimeval tv;

gettimeofday(&tv,NULL);

longtime=  tv.tv_sec * 1000 + tv.tv_usec / 1000;

returntime;

}

//舵机驱动函数

voidstart_pulse(intpwm_id,intfreq,intvalue){

intstep = 0;

step = pwmfreq_set(pwm_id, freq);

//printf(“PWM%d set freq %d and valid duty cycle range [0, %d]\n”, pwm_id, freq, step);

if(step > 0){

//printf(“PWM%d test with duty cycle %d\n”, pwm_id, value);

analogWrite(pwm_id, value);

delay(50);

}

}

//signal回调函数,监听中断信号,做一些状态复位工作

voidsigroutine(intdunno) {

switch(dunno) {

case2:

printf(“Get a signal – SIGINT \n”);

reset();

analogWrite(6,0);

analogWrite(5,0);

digitalWrite(pin_led,LOW);

exit(0);

break;

}

}

voidloop(){}


 类似资料: