opencv人脸检测代码应用与分析(PC端和Android端)

本文介绍OpenCV中基于Viola&Jones方法的人脸检测程序流程,包括加载分类器、图像处理及人脸检测步骤,并阐述了OpenCV在Android平台上的移植方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(1)OpenCV人脸检测C++程序流程:

OpenCV的人脸检测程序采用了Viola & Jones人脸检测方法,主要是调用训练好的瀑布级联分类器cascade来进行模式匹配。

cvHaarDetectObjects先将图像灰度化,根据传入参数判断是否进行canny边缘处理(默认不使用),再进行匹配。匹配后收集找出的匹配块,过滤噪声,计算相邻个数如果超过了规定值(传入的min_neighbors)就当成输出结果,否则删去。

匹配循环:将匹配分类器放大scale(传入值)倍,同时原图缩小scale倍,进行匹配,直到匹配分类器的大小大于原图,则返回匹配结果。匹配的时候调用cvRunHaarClassifierCascade来进行匹配,将所有结果存入CvSeq* pcvSeqFaces(可动态增长元素序列),将结果传给cvHaarDetectObjects。

cvRunHaarClassifierCascade函数整体是根据传入的图像和cascade来进行匹配。并且可以根据传入的cascade类型不同(树型、stump(不完整的树)或其他的),进行不同的匹配方式。函数cvRunHaarClassifierCascade 用于对单幅图片的检测。在函数调用前首先利用cvSetImagesForHaarClassifierCascade设定积分图和合适的比例系数 (>= 窗口尺寸)。当分析的矩形框全部通过级联分类器每一层的时返回正值(这是一个候选目标),否则返回0或负值。

Haar分类器的训练是独立于人脸检测过程的。分类器的训练分为两个阶段:

A.创建样本,用OpenCV自带的creatsamples.exe完成。

B.训练分类器,生成xml文件,由OpenCV自带的haartraining.exe完成。

同时,OpenCV中采用的训练算法adaboost是gentle adaboost,为最适合人脸检测的方案。

基于 OpenCV 的人脸检测主要完成 3 部分功能 , 即加载分类器、 加载待检测图象以及检测并标示。本算法使用 OpenCV 中提供的 “ haarcascade_frontalface_alt. xml ” 文件存储的目标检测分类 , 用 cvLoad 函数载入后 , 进行强制类型转换。OpenCV 中提供的用于检测图像中目标的函数是 cvHaarDetectObjects , 该函数使用指针对某目标物体 ( 如人脸) 训练的级联分类器在图象中找到包含目标物体的矩形区域 , 并将这些区域作为一序列的圆形框返回,实现代码如下 :

#include <opencv2/opencv.hpp> 
#include <cstdio> 
#include <cstdlib> 
#include <Windows.h>
int main(){
	// 加载Haar特征检测分类器 
    // haarcascade_frontalface_alt.xml系OpenCV自带的分类器,下面是文件路径 
       const char *pstrCascadeFileName ="D:\\Program Files\\opencv\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml";
	// 创建瀑布级联分类器cascade来进行模式匹配                                       
    CvHaarClassifierCascade *pHaarCascade =NULL; 
    pHaarCascade =(CvHaarClassifierCascade*)cvLoad(pstrCascadeFileName); 
	// 载入图像
	// 创建一个IpIImage 图像数据结构进行处理
    const char *pstrImageName ="101.jpg"; 
    IplImage *pSrcImage = cvLoadImage(pstrImageName,CV_LOAD_IMAGE_UNCHANGED); 
    // 输出图像 
    IplImage *pGrayImage =cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); 
    cvCvtColor(pSrcImage, pGrayImage,CV_BGR2GRAY); 
    // 人脸识别与标记 
    if (pHaarCascade != NULL) 
    {        
        CvScalar FaceCirclecolors[] =  
        { //标记人脸框的颜色
            {{0, 0, 255}}, {{0, 128,255}},  {{0, 255, 255}},  {{0, 255, 0}},  {{255, 128, 0}},  {{255, 255, 0}}, {{255, 0, 0}},  {{255, 0, 255}} 
        }; 
        //创建一个新的内存存储区 , 参数为0 则采用默认设置
        CvMemStorage *pcvMStorage =cvCreateMemStorage(0); 
        cvClearMemStorage(pcvMStorage); 
        // 处理的开始和结束时间 
        DWORD dwTimeBegin, dwTimeEnd; 
        dwTimeBegin = GetTickCount(); 
			  // 人脸识别核心函数(opencv自带)cvHaarDetectObjects
              //返回一个包含检测结果(图片中的位置)信息的结构体数据 pcvSeqFaces
              //具体算法实现见opencv源码D:\ProgramFiles\opencv\opencv\sources\modules\objdetect\src\haar.cpp
        CvSeq *pcvSeqFaces = cvHaarDetectObjects(pGrayImage,pHaarCascade, pcvMStorage); 
        dwTimeEnd = GetTickCount(); 
        printf("人脸个数: %d   识别用时: %d ms\n", pcvSeqFaces->total,dwTimeEnd - dwTimeBegin); 
        // 开始标记
        for(int i = 0; i<pcvSeqFaces->total; i++) 
        {  // 逐次获取位置信息
            CvRect* r =(CvRect*)cvGetSeqElem(pcvSeqFaces, i); 
            CvPoint center; 
            int radius; 
            center.x = cvRound((r->x +r->width * 0.5)); 
            center.y = cvRound((r->y +r->height * 0.5));  
            radius = cvRound((r->width +r->height) * 0.25); 
            cvCircle(pSrcImage, center, radius,FaceCirclecolors[i % 8], 2); 
        } 
       cvReleaseMemStorage(&pcvMStorage); //释放内存存储区
    } 
	const char *pstrWindowsTitle = "人脸识别"; 
	//创建处理结果展示窗口
    cvNamedWindow(pstrWindowsTitle,CV_WINDOW_AUTOSIZE); 
    cvShowImage(pstrWindowsTitle,pSrcImage); 
    cvWaitKey(0); 
    cvDestroyWindow(pstrWindowsTitle);  //释放窗口资源
    cvReleaseImage(&pSrcImage);   //释放图片资源
    cvReleaseImage(&pGrayImage); 
return 0;
}

 

(2)OpenCV 移植到Android

首先,需确定搭建配置完成 Android 开发环境所需的 JDK、EclipseIDE、Android SDK 和 ADT。其次,确定安装 CDT 插件,以供 Eclipse 能够开发 C++。

Android 需要使用JNI 编写本地代码,并使用 Android NDK进行交叉编译。Android NDK 有多种版本,本实验使用的计算机是 32 位的 Windows 操作系统,所以选择版本为 android-ndk-r10-windows-x86。

OpenCV从 2.2 版本后支持在 Android 下开发,本系统采用的是 OpenCV-2.4.10 版本。在该版本解压缩后的文件夹下,有4 个子文件夹,它们分别是 sdk、samples、doc 和apk。Sdk 是OpenCV所需的类库;samples 是 OpenCV 应用的例子;doc 是OpenCV 类库的使用说明、api文档和一些图像等;apk 是一些对应于各内核版本的 OpenCV_2.4. 10_Manager_2.9 应用安装包。该应用是用来管理 OpenCV 类库,若手机中无此应用,或未将该应用移植到自己编写的 Android 应用程序中,将无法加载 OpenCV

类库,从而导致程序无法运行下去。

在 Eclipse 上需导入进 OpenCV-2.4. 10 里的 sdk 文件夹下所有内容作为一个项目,在所需开发的项目的 Properties Android 里添加库,该库即为导入进来的 OpenCV 的sdk 项目。

在 Android 平台上使用 OpenCV 进行人脸检测,需要使用NDK 工具编译使用 JNI 编写的本地代码,并将编译后生成的 .so动态库加载到 Android 应用程序中。因此实现过程分为两部分:(1) 在Android 应用程序中编写相关的 Java 代码;(2) 是使用JNI编写本地代码并调用 OpenCV 中的相关函数,然后通过 NDK 编译生成可供 Java 代码调用的动态库,最后通过 Android SDK 开发成应用程序。实现过程如图 4 所示。

 

图 4实现过程图

主要代码(主要方法说明,具体见源码--即OpenCV-2.4.10-android-sdk(自行下载)提供的示例工程):

public class FdActivity extends Activityimplements CvCameraViewListener2 {
    //动态检测回调方法,进行加载库、IO、检测准备等操作
   private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
       @Override
       public void onManagerConnected(int status) {
           switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    //加载本地动态检测库,mOpenCvCameraView对象初始化后调用
                   System.loadLibrary("detection_based_tracker");
                    try {
                        //加载cascade分类器资源,涉及到IO
                        InputStream is =getResources().openRawResource(R.raw.lbpcascade_frontalface);
                        File cascadeDir =getDir("cascade", Context.MODE_PRIVATE);
                       mCascadeFile =new File(cascadeDir, "lbpcascade_frontalface.xml");
                        FileOutputStream os =new FileOutputStream(mCascadeFile);
                        byte[] buffer = newbyte[4096];
                        int bytesRead;
                        while ((bytesRead =is.read(buffer)) != -1) {
                            os.write(buffer, 0,bytesRead);
                        }
                        is.close();
                        os.close();
                       mJavaDetector = newCascadeClassifier(mCascadeFile.getAbsolutePath());
                        if(mJavaDetector.empty()) {
                            Log.e(TAG,"Failed to load cascade classifier");
                            mJavaDetector =null;
                        } else
                        mNativeDetector = newDetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
                       cascadeDir.delete();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e(TAG, "Failedto load cascade. Exception thrown: " + e);
                    }
                   mOpenCvCameraView.enableView(); //检测准备完毕
                } break;
                default:
                {
                   super.onManagerConnected(status);
                } break;
           }
       }
    };
    //构造方法
    public FdActivity() {…}

	//重写方法,android资源初始化
    @Override
    public void onCreate(Bundle savedInstanceState) {…}
    //开启摄像头检测人脸相关配置方法
	public voidonCameraViewStarted(int width, int height) {…}
	//停用摄像头检测人脸相关配置方法
	public void onCameraViewStopped(){…}
	//实现动态从摄像头取帧读取,转换为灰度图进行分析,检测出人脸之后圈出显示
    public Mat onCameraFrame(CvCameraViewFrameinputFrame) {
        mRgba = inputFrame.rgba();//转化为RGB格式
        mGray = inputFrame.gray();//转化为灰度图
        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height *mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize =Math.round(height * mRelativeFaceSize);
            }
            mNativeDetector.setMinFaceSize(mAbsoluteFaceSize);//设置检测人脸大小
        }
        MatOfRect faces = new MatOfRect();
        if (mDetectorType == JAVA_DETECTOR) {
            if (mJavaDetector != null)
		//多人脸检测
               mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, newSize(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
        }
        else if (mDetectorType ==NATIVE_DETECTOR) {
            if (mNativeDetector != null)
		//单个人脸检测
                mNativeDetector.detect(mGray,faces);
        }
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i <facesArray.length; i++)
            Core.rectangle(mRgba,facesArray[i].tl(), facesArray[i].br(),  FACE_RECT_COLOR,3);
        return mRgba;
	}
   private void setMinFaceSize(float faceSize) {…} //设置检测人脸大小方法
   private void setDetectorType(int type) {…} //设置检测器类型(是否检测多个人脸)方法
}

待修正,请见谅

#include &quot;stdafx.h&quot; #include &quot;cv.h&quot; #include &quot;highgui.h&quot; #include &lt;stdio.h&gt; int _tmain(int argc, _TCHAR* argv[]) { CvCapture* capture=0; /*初始化一个视频捕获操作。告诉底层的捕获api我想从Capture1.avi中捕获图片, 底层api将检测并选择相应的解码器并做好准备工作*/ capture = cvCaptureFromFile( &quot;F:\\1.avi&quot;); //设置要读的视频(avi格式) static CvMemStorage* storage = 0; static CvHaarClassifierCascade* cascade = 0; cascade = (CvHaarClassifierCascade*)cvLoad(&quot;haarcascade_frontalface_alt.xml&quot;,0,0,0); if( !cascade || !capture ) return -1; storage = cvCreateMemStorage(0); /*创建一个窗口,用&ldquo;Video&rdquo;作为窗口的标识符*/ cvNamedWindow( &quot;Video&quot;,1); /*如果初始化失败,那么capture为空指针,程序停止,否则进入捕获循环*/ if( capture ) { for(;;) { IplImage* frame = cvQueryFrame( capture ); IplImage* img = NULL; CvSeq* faces; if( !frame ) break; img = cvCloneImage(frame); img-&gt;origin = 0; if( frame-&gt;origin ) cvFlip(img,img); cvClearMemStorage( storage ); //目标检测 faces = cvHaarDetectObjects( img, cascade, storage,1.1, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(20, 20) ); for( int i = 0; i &lt; (faces ? faces-&gt;total : 0); i++ ) { CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); cvRectangle( img, cvPoint(r-&gt;x,r-&gt;y), cvPoint(r-&gt;x+r-&gt;width,r-&gt;y+r-&gt;height), CV_RGB(255,0,0), 1); } cvShowImage( &quot;Video&quot;, img ); //设置每帧图像的间隔 Sleep(50); /*如果你敲了键盘,就退出程序,否则继续捕获下一帧*/ if( cvWaitKey(10)&gt;0 ) break; } /*退出之前结束底层api的捕获操作,比如会使得别的程序无法访问已经被它们打开的文件*/ cvReleaseCapture( &amp;capture;); } /*销毁窗口*/ cvDestroyWindow(&quot;Video&quot;); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值