(4)opencv在android平台上实现 物体跟踪

时间:2023-02-04 11:23:35

最近项目时间很紧,抓紧时间集中精力去研究android平台的opencv里的物体跟踪技术


其他几篇文章有时间再去完善吧


从网上找到了一些实例代码,我想采取的学习方法是研究实例代码和看教程相结合,教程是ndk编程方面的编程规则等、opencv人脸识别、物体跟踪这一块的教程


(1)人脸检测与跟踪库 asmlibrary   分析和研究

http://www.oschina.net/p/asmlibrary

http://yaohan.sinaapp.com/topic/3/asmlibrary#comments



我下载了6.0这个版本,往eclipse里导入的时候要注意一些问题,记得导入Opencv-Library,在C++的paths and symbols的include里把需要的库加进去,然后还可能遇到其他问

题,读者自行百度或者留言给我吧,一起讨论研究一下!


把这个项目导入eclipse之后,观察它的结构:


src文件夹下有两个java文件:  ASMFit.java, ASMLibraryActivity.java 

jni文件夹下有 so文件夹,Android.mk,Application.mk,asmfitting.h,asmlibrary.h,DemoFit.cpp,vjfacedetect.h

在res文件夹下发现了一个文件夹 raw,第一次见,然后百度了一下,


assets和res下面raw文件的使用不同点

assets下面的文件不会被编译,通过路径可以去访问其中的内容。raw中文件会自动编译,我们可以在R.java文件中找到对应的ID


其中比较重要的是获取到Assets和Raw文件夹中的资源方法:

      Assets:     AssetManager assetManager = getAssets();

      Raw:        InputStream inputStream = getResources().openRawResource(R.raw.demo); 



把代码贴进来研究



java部分:


(1)ASMFit.java

package org.asmlibrary.fit;

import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;

import android.util.Log;

public class ASMFit {

public ASMFit(){}

/**
* This function can only be used after nativeInitFastCascadeDetector()
* @param imageGray original gray image
* @param faces all faces' feature points
* @return true if found faces, false otherwise
*/
public boolean fastDetectAll(Mat imageGray, Mat faces){
return nativeFastDetectAll(imageGray.getNativeObjAddr(),
faces.getNativeObjAddr());
}

/**
* This function can only be used after nativeInitCascadeDetector()
* @param imageGray original gray image
* @param face all faces' feature points
* @return true if found faces, false otherwise
*/
public boolean detectAll(Mat imageGray, Mat faces){
return nativeDetectAll(imageGray.getNativeObjAddr(),
faces.getNativeObjAddr());
}

/**
* This function can only be used after nativeInitCascadeDetector()
* @param imageGray original gray image
* @param faces only one face's feature points
* @return true if found faces, false otherwise
*/
public boolean detectOne(Mat imageGray, Mat face){
return nativeDetectOne(imageGray.getNativeObjAddr(),
face.getNativeObjAddr());
}

public void fitting(Mat imageGray, Mat shapes){
nativeFitting(imageGray.getNativeObjAddr(),
shapes.getNativeObjAddr());
}

public boolean videoFitting(Mat imageGray, Mat shape, long frame){
return nativeVideoFitting(imageGray.getNativeObjAddr(),
shape.getNativeObjAddr(), frame);
}

public static native boolean nativeReadModel(String modelName);

/**
* @param cascadeName could be haarcascade_frontalface_alt2.xml
* @return
*/
public static native boolean nativeInitCascadeDetector(String cascadeName);
public static native void nativeDestroyCascadeDetector();

/**
* @param cascadeName could be lbpcascade_frontalface.xml
* @return
*/
public static native boolean nativeInitFastCascadeDetector(String cascadeName);
public static native void nativeDestroyFastCascadeDetector();

public static native void nativeInitShape(long faces);

private static native boolean nativeDetectAll(long inputImage, long faces);
private static native boolean nativeDetectOne(long inputImage, long face);
private static native boolean nativeFastDetectAll(long inputImage, long faces);

private static native void nativeFitting(long inputImage, long shapes);
private static native boolean nativeVideoFitting(long inputImage, long shape, long frame);

}


(2)ASMLibraryActivity.java

package org.asmlibrary.fit;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.core.Scalar;
import org.opencv.core.Point;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.asmlibrary.fit.R;
import org.asmlibrary.fit.ASMFit;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
import android.content.res.Configuration;

public class ASMLibraryActivity extends Activity implements CvCameraViewListener2{

private static final String TAG = "ASMLibraryDemo";

private Mat mRgba;
private Mat mGray;
private Mat mGray2;
private File mCascadeFile;
private File mFastCascadeFile;
private File mModelFile;
private ASMFit mASMFit;
private long mFrame;
private boolean mFlag;
private boolean mPortrait = true;
private boolean mFastDetect = false;
private Mat mShape;
private static final Scalar mColor = new Scalar(255, 0, 0);
private MenuItem mHelpItem;
private MenuItem mDetectItem;
private MenuItem mOrieItem;
private MenuItem mCameraitem;
private CameraBridgeViewBase mOpenCvCameraView;
private int mCameraIndex = CameraBridgeViewBase.CAMERA_ID_ANY;

public ASMLibraryActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
}

private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
private File getSourceFile(int id, String name, String folder){
File file = null;
try {
InputStream is = getResources().openRawResource(id);
File cascadeDir = getDir(folder, Context.MODE_PRIVATE);
file = new File(cascadeDir, name);
FileOutputStream os = new FileOutputStream(file);

byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
}catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Failed to load file " + name + ". Exception thrown: " + e);
}

return file;

}

@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");

// Load native library after(!) OpenCV initialization
System.loadLibrary("asmlibrary");
System.loadLibrary("jni-asmlibrary");

mASMFit = new ASMFit();

mModelFile = getSourceFile(R.raw.my68_1d, "my68_1d.amf", "model");
if(mModelFile != null)
mASMFit.nativeReadModel(mModelFile.getAbsolutePath());


mCascadeFile = getSourceFile(R.raw.haarcascade_frontalface_alt2,
"haarcascade_frontalface_alt2.xml", "cascade");
if(mCascadeFile != null)
mASMFit.nativeInitCascadeDetector(mCascadeFile.getAbsolutePath());

mFastCascadeFile = getSourceFile(R.raw.lbpcascade_frontalface,
"lbpcascade_frontalface.xml", "cascade");
if(mFastCascadeFile != null)
mASMFit.nativeInitFastCascadeDetector(mFastCascadeFile.getAbsolutePath());

//test image alignment
// load image file from application resources
File JPGFile = getSourceFile(R.raw.gump, "gump.jpg", "image");

Mat image = Highgui.imread(JPGFile.getAbsolutePath(), Highgui.IMREAD_GRAYSCALE);
Mat shapes = new Mat();

if(mASMFit.detectAll(image, shapes) == true)
{
/*
for(int i = 0; i < shapes.row(0).cols()/2; i++)
{
Log.d(TAG, "before points:" +
shapes.get(0, 2*i)[0] +"," +shapes.get(0, 2*i+1)[0]);
}
*/

mASMFit.fitting(image, shapes);

/*
for(int i = 0; i < shapes.row(0).cols()/2; i++)
{
Log.d(TAG, "after points:" +
shapes.get(0, 2*i)[0] +"," +shapes.get(0, 2*i+1)[0]);
}
*/
}

mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

setContentView(R.layout.face_detect_surface_view);

mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.fd_activity_surface_view);
mOpenCvCameraView.setCvCameraViewListener(this);
mFrame = 0;
mFlag = false;
}

@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}

@Override
public void onResume()
{
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
mFrame = 0;
mFlag = false;
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "called onCreateOptionsMenu"+mFastDetect);
mCameraitem = menu.add("Toggle Front/Back");
mOrieItem = menu.add("Toggle Portrait");
if(mFastDetect == true)
mDetectItem = menu.add("CascadeDetector");
else
mDetectItem = menu.add("FastCascadeDetector");
mHelpItem = menu.add("About ASMLibrary");
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
if (item == mHelpItem)
new AlertDialog.Builder(this).setTitle("About ASMLibrary")
.setMessage("ASMLibrary -- A compact SDK for face alignment/tracking\n" +
"Copyright (c) 2008-2011 by Yao Wei, all rights reserved.\n" +
"Contact: njustyw@gmail.com\n")
.setPositiveButton("OK", null).show();
else if(item == mDetectItem)
{
mFastDetect = !mFastDetect;
}
else if(item == mOrieItem)
{
mPortrait = !mPortrait;
}
else if(item == mCameraitem)
{
if(mCameraIndex == CameraBridgeViewBase.CAMERA_ID_ANY ||
mCameraIndex == CameraBridgeViewBase.CAMERA_ID_BACK)
mCameraIndex = CameraBridgeViewBase.CAMERA_ID_FRONT;
else
mCameraIndex = CameraBridgeViewBase.CAMERA_ID_BACK;
mOpenCvCameraView.setCameraIndex(mCameraIndex);
}
return true;
}

@Override
public void onDestroy() {
super.onDestroy();
mOpenCvCameraView.disableView();
}

public void onCameraViewStarted(int width, int height) {
mGray = new Mat();
mGray2 = new Mat();
mRgba = new Mat();
mShape = new Mat();
mFrame = 0;
mFlag = false;
}

public void onCameraViewStopped() {
mGray.release();
mRgba.release();
}

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

mRgba = inputFrame.rgba();
mGray = inputFrame.gray();

if(mPortrait ==true)
Core.transpose(mGray, mGray2);
else
mGray2 = mGray;

//WindowManager manager = getWindowManager();
//int width = manager.getDefaultDisplay().getWidth();
//int height = manager.getDefaultDisplay().getHeight();
//Log.d(TAG, "屏幕大小" + width + "x" + height);

if(mFrame == 0 || mFlag == false)
{
Mat detShape = new Mat();
if(mFastDetect)
mFlag = mASMFit.fastDetectAll(mGray2, detShape);
else
mFlag = mASMFit.detectAll(mGray2, detShape);
if(mFlag) mShape = detShape.row(0);
}

if(mFlag)
{
mFlag = mASMFit.videoFitting(mGray2, mShape, mFrame);
}

if(mFlag)
{
if(mPortrait == true)
{
int nPoints = mShape.row(0).cols()/2;
for(int i = 0; i < nPoints; i++)
{
double x = mShape.get(0, 2*i)[0];
double y = mShape.get(0, 2*i+1)[0];
Point pt = new Point(y, x);

Core.circle(mRgba, pt, 3, mColor);
}
}
else
{
int nPoints = mShape.row(0).cols()/2;
for(int i = 0; i < nPoints; i++)
{
Point pt = new Point(mShape.get(0, 2*i)[0], mShape.get(0, 2*i+1)[0]);
Core.circle(mRgba, pt, 3, mColor);
}
}
}

mFrame ++;

return mRgba;
}
}


C++部分(jni)

(1)Android.mk

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE := asmlibrary
LOCAL_SRC_FILES := so/$(TARGET_ARCH_ABI)/libasmlibrary.so
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

#OPENCV_CAMERA_MODULES:=off
#OPENCV_INSTALL_MODULES:=off
#OPENCV_LIB_TYPE:=SHARED
include E:\android-eclipse\OpenCV-2.4.8-android-sdk/sdk/native/jni/OpenCV.mk

LOCAL_SRC_FILES := DemoFit.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_CFLAGS += -DOPENCV_OLDER_VISION
LOCAL_LDLIBS += -llog -ldl

LOCAL_MODULE := jni-asmlibrary

LOCAL_SHARED_LIBRARIES := asmlibrary

include $(BUILD_SHARED_LIBRARY)

(2)Application.mk

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a armeabi x86 mips
APP_PLATFORM := android-8

(3)asmfitting.h

#ifndef _ASM_FITTING_H_
#define _ASM_FITTING_H_

#include "asmlibrary.h"

/** Wrapped Class for face alignment/tracking using active shape model */
class ASMLIB asmfitting
{
public:
/** Constructor */
asmfitting();

/** Destructor */
~asmfitting();

/**
Process face alignment on image. (Only for one face box)
@param shape the point features that carries initial shape and also restores result after fitting
@param image the image resource
@param n_iteration the number of iteration during fitting
*/
void Fitting(asm_shape& shape, const IplImage* image, int n_iteration = 30);

/**
Process face alignment on image. (For multi-face boxes)
@param shapes all shape datas that carry the fitting result
@param n_shapes the number of human face
@param image the image resource
@param n_iteration the number of iteration during fitting
*/
void Fitting2(asm_shape* shapes, int n_shapes, const IplImage* image, int n_iteration = 30);

/**
Process face tracking on video/camera.
@param shape the point features that carries initial shape and also restores result after fitting
@param image the image resource
@param frame_no one certain frame number of video/camera
@param bopticalflow whether to use optical flow or not?
@param n_iteration the number of iteration during fitting
@return false on failure, true otherwise.
*/
bool ASMSeqSearch(asm_shape& shape, const IplImage* image,
int frame_no = 0, bool bopticalflow = false, int n_iteration = 30);

/**<
Get the Average Viola-Jone Box.
*/
const asm_shape GetMappingDetShape()const { return m__VJdetavshape;}

/**<
Get the width of mean face.
*/
const double GetMeanFaceWidth()const{ return m_model.GetMeanShape().GetWidth(); }

/**<
Get raw ptr of asm_model.
*/
const asm_model* GetModel()const { return &m_model; }

/**
Read model data from file.
@param filename the filename that stores the model
@return false on failure, true otherwise
*/
bool Read(const char* filename);

private:

/**
Apply optical flow between two successive frames.
@param shape it carries initial shape and also restores result after fitting
@param grayimage the image resource.
*/
void OpticalFlowAlign(asm_shape& shape, const IplImage* grayimage);

private:
asm_model m_model; /**<active shape model to be trained */
int *m_edge_start; /**< Starting index of edges */
int *m_edge_end; /**< Ending index of edges */
int m_nEdge; /**< Number of edges */
asm_shape m__VJdetavshape; /**< average mapping shape relative to VJ detect box*/
scale_param m_param; /**< point index of left and right side in the face template*/
bool m_flag; /**< Does the image contain face? */
double m_dReferenceFaceWidth; /**< reference face width */

private:
IplImage* __lastframe; /**< Cached variables for optical flow */
IplImage* __pyrimg1; /**< Cached variables for optical flow */
IplImage* __pyrimg2; /**< Cached variables for optical flow */
Point2D32f* __features1; /**< Cached variables for optical flow */
Point2D32f* __features2; /**< Cached variables for optical flow */
char* __found_feature; /**< Cached variables for optical flow */
float* __feature_error; /**< Cached variables for optical flow */
};

#endif //_ASM_FITTING_H_


(4)asmlibrary.h
#ifndef _ASM_LIBRARY_H_
#define _ASM_LIBRARY_H_

#include <stdio.h>

class asm_shape;
class asm_profile;
class asm_model;
struct profile_Nd_model;
struct profile_lbp_model;
struct CvMat;
struct _IplImage;

typedef unsigned char uchar;
typedef struct _IplImage IplImage;

#ifdef WIN32
#ifdef ASMLIBRARY_EXPORTS
#define ASMLIB __declspec(dllexport)
#else
#define ASMLIB __declspec(dllimport)
#endif
#else
#define ASMLIB
#endif

/**
* Predefined local texture (profile) types.
* <ul>
* <li>PROFILE_1D: use only the pixels along the normal vector in the contour.</li>
* <li>PROFILE_2D: use the pixels located at the recentage.</li>
* <li>PROFILE_LBP: use the pixels processed with LBP-operator.</li>
* </ul>
**/
enum ASM_PROFILE_TYPE {PROFILE_1D, PROFILE_2D, PROFILE_LBP};

#ifdef __cplusplus
extern "C"{
#endif

/**
Initialize shape from the detected box.
@param shape the returned initial shape
@param det_shape the detected box calling by \a asm_vjfacedetect::\a Detect()
@param ref_shape the average mean shape
@param refwidth the width of average mean shape
*/
ASMLIB void InitShapeFromDetBox(asm_shape &shape, const asm_shape& det_shape,
const asm_shape &ref_shape, double refwidth);

#ifdef __cplusplus
}
#endif

/** Class for 2d point. */
typedef struct Point2D32f
{
float x;
float y;
}
Point2D32f;

/** Class for 2d shape data. */
class ASMLIB asm_shape
{
public:
/** Constructor */
asm_shape();

/** Copy Constructor */
asm_shape(const asm_shape &v);

/** Destructor */
~asm_shape();

/**
Access elements by \a CvPoint2D32f \a pt = \a shape[\a i] to get \a i-th point in the shape.
@param i Index of points
@return Point at the certain index
*/
const Point2D32f operator [](int i)const{ return m_vPoints[i]; }

/**
Access elements by \a CvPoint2D32f \a pt = \a shape[\a i] to get \a i-th point in the shape.
@param i Index of points
@return Point at the certain index
*/
Point2D32f& operator [](int i){ return m_vPoints[i]; }

/**
Get the number of points.
@return Number of points
*/
inline const int NPoints()const{ return m_nPoints; }

/**
Override of operator =
*/
asm_shape& operator =(const asm_shape &s);

/**
Override of operator =.
*/
asm_shape& operator =(double value);

/**
Override of operator +
*/
const asm_shape operator +(const asm_shape &s)const;

/**
Override of operator +=
*/
asm_shape& operator +=(const asm_shape &s);

/**
Override of operator -
*/
const asm_shape operator -(const asm_shape &s)const;

/**
Override of operator -=
*/
asm_shape& operator -=(const asm_shape &s);

/**
Override of operator *
*/
const asm_shape operator *(double value)const;

/**
Override of operator *=
*/
asm_shape& operator *=(double value);

/**
Override of operator *
*/
double operator *(const asm_shape &s)const;

/**
Override of operator /
*/
const asm_shape operator /(double value)const;

/**
Override of operator /=
*/
asm_shape& operator /=(double value);

/**
Release memory.
*/
void Clear();

/**
Allocate memory.
@param length Number of of shape points
*/
void Resize(int length);

/**
Read points from file.
@param filename the filename the stored shape data
@return true on pts format, false on asf format, exit otherwise
*/
bool ReadAnnotations(const char* filename);

/**
Read points from asf format file.
@param filename the filename the stored shape data
*/
void ReadFromASF(const char*filename);

/**
Read points from pts format file.
@param filename the filename the stored shape data
*/
void ReadFromPTS(const char*filename);

/**
Write shape data into file stream.
@param f stream to write to
*/
void Write(FILE* f);

/**
Read shape data from file stream.
@param f stream to read from
*/
void Read(FILE* f);

/**
Calculate minimum \f$x\f$-direction value of shape.
*/
const double MinX()const;

/**
Calculate minimum \f$y\f$-direction value of shape.
*/
const double MinY()const;

/**
Calculate maximum \f$x\f$-direction value of shape.
*/
const double MaxX()const;

/**
Calculate maximum \f$y\f$-direction value of shape.
*/
const double MaxY()const;

/**
Calculate the left and right index for \f$x\f$-direction in the shape.
@param ileft the index of points in \f$x\f$-direction which has the minimum x
@param iright the index of points in \f$x\f$-direction which has the maximum x
*/
void GetLeftRight(int& ileft, int& iright)const;

/**
Calculate width of shape.
@param ileft Index of points in \f$x\f$-direction which has the minimum x
@param iright Index of points in \f$x\f$-direction which has the maximum x
*/
const double GetWidth(int ileft = -1, int iright = -1)const;

/**
Calculate height of shape.
*/
const double GetHeight()const { return MaxY()-MinY(); }

/**
Calculate center of gravity for shape.
@param x Value of center in \f$x\f$-direction
@param y Value of center in \f$y\f$-direction
*/
void COG(double &x, double &y)const;

/**
Translate the shape to make its center locate at (0, 0).
*/
void Centralize();

/**
Translate the shape.
@param x Value of translation factor in \f$x\f$-direction
@param y Value of translation factor in \f$y\f$-direction
*/
void Translate(double x, double y);

/**
Scale shape by an uniform factor.
@param s Scaling factor
*/
void Scale(double s);

/**
Rotate shape by anti clock-wise.
@param theta Angle to be rotated
*/
void Rotate(double theta);

/**
Scale shape in x and y direction respectively.
@param sx Scaling factor in \f$x\f$-direction
@param sy Scaling factor in \f$y\f$-direction
*/
void ScaleXY(double sx, double sy);

/**
Normalize shape (zero_mean_unit_length) so that its center locates at (0, 0) and its \f$L2\f$-norm is 1.
@return the \f$L2\f$-norm of original shape
*/
double Normalize();


enum{ LU, SVD, Direct };

/**
Calculate the similarity transform between one shape and another reference shape.
Where the similarity transform is:
<BR>
\f$T(a,b,tx,ty) = [a \ -b \ Tx; b \ a \ Ty ; 0 \ 0 \ 1]\f$.
@param ref_shape the reference shape
@param a will return \f$ s \times cos(theta) \f$ in form of similarity transform
@param b will return \f$ s \times sin(theta) \f$ in form of similarity transform
@param tx will return \f$ Tx \f$ in form of similarity transform
@param ty will return \f$ Ty \f$ in form of similarity transform
@param method Method of similarity transform
*/
void AlignTransformation(const asm_shape &ref_shape, double &a, double &b,
double &tx, double &ty, int method = SVD)const;

/**
Align the shape to the reference shape.
@param ref_shape the reference shape
@param method method of similarity transform
*/
void AlignTo(const asm_shape &ref_shape, int method = SVD);

/**
Transform Shape using the similarity transform \f$T(a,b,tx,ty)\f$.
*/
void TransformPose(double a, double b, double tx, double ty);

/**
Calculate the angular bisector between two lines \f$Pi-Pj\f$ and \f$Pj-Pk\f$.
@param i the index of point vertex
@param j the index of point vertex
@param k the index of point vertex
@return Angular bisector vector in form of \f$(cos(x), sin(x))^T\f$
*/
Point2D32f CalcBisector(int i, int j, int k)const;

/**
Calculate the Euclidean norm (\f$L2\f$-norm).
@return Euclidean norm
*/
double GetNorm2()const;

/**
Calculate the normal vector at certain vertex around the shape contour.
@param cos_alpha the normal vector in \f$x\f$-direction
@param sin_alpha the normal vector in \f$y\f$-direction
@param i the index of point vertex
*/
void CalcNormalVector(double &cos_alpha, double &sin_alpha, int i)const;

/**
Convert from OpenCV's \a CvMat to class asm_shape
@param mat \a CvMat that converted from
*/
void CopyFrom(const CvMat* mat);

/**
Convert from class asm_shape to OpenCV's CvMat.
@param mat CvMat that converted to
*/
void CopyTo(CvMat* mat)const;

private:
void Transform(double c00, double c01, double c10, double c11);

private:
Point2D32f* m_vPoints; /**< point data */
int m_nPoints; /**< number of points */
};

/** Left and Right index in \f$x\f$-direction of shape */
typedef struct scale_param
{
int left; /**< Index of points in \f$x\f$-direction which has the minimum x */
int right; /**< Index of points in \f$x\f$-direction which has the maximum x */
}scale_param;


/** Class for active shape model. */
class ASMLIB asm_model
{
public:
/**
Constructor
*/
asm_model();

/**
Destructor
*/
~asm_model();

/**
Image alignment/fitting with an initial shape.
@param shape the point features that carries initial shape and also restores result after fitting
@param grayimage the gray image resource
@param max_iter the number of iteration
@param param the left and right index for \f$x\f$-direction in the shape (Always set \a NULL )
@return false on failure, true otherwise
*/
bool Fit(asm_shape& shape, const IplImage *grayimage,
int max_iter = 30, const scale_param* param = NULL);

/**
Write model data to file stream.
@param f stream to write to
*/
void WriteModel(FILE* f);

/**
Read model data from file stream.
@param f stream to read from
*/
void ReadModel(FILE* f);

/**
Get mean shape of model.
*/
const asm_shape& GetMeanShape()const { return m_asm_meanshape; }

/**
Get modes of shape distribution model (Will be calculated in shape's PCA)
*/
const int GetModesOfModel()const { return m_nModes;}

/**
Get the width of mean shape [Identical to \a m_asm_meanshape.\a GetWidth()].
*/
const double GetReferenceWidthOfFace()const { return m_dReferenceFaceWidth; }

private:

/**
Get the optimal offset at one certain point vertex during the process of
best profile matching (work for 1d/2d profile model).
@param image the image resource
@param ilev one certain pyramid level
@param shape the shape data
@param ipoint the index of point vertex
@param cos_alpha the normal vector in \f$x\f$-direction
@param sin_alpha the normal vector in \f$y\f$-direction
@return offset bias from \a Shape[\a iPoint]
*/
int FindBestOffsetForNd(const IplImage* image, int ilev,
const asm_shape& shape, int ipoint,
double& cos_alpha, double& sin_alpha);

/**
Get the optimal offset at one certain point vertex during the process of
best profile matching (work for lbp profile model).
@param lbp_img the target image processed with LBP
@param nrows the height of \a lbp_img
@param ncols the width of \a lbp_img
@param ilev one certain pyramid level
@param shape the shape data
@param ipoint the index of point vertex
@param xoffset the returned offset in \f$x\f$-direction away from \a Shape[\a iPoint]
@param yoffset the returned offset in \f$y\f$-direction away from \a Shape[\a iPoint]
*/
void FindBestOffsetForLBP(const int* lbp_img, int nrows, int ncols, int ilev,
const asm_shape& shape, int ipoint, int& xoffset, int& yoffset);

/**
Update shape by matching the image profile to the model profile.
@param update_shape the updated shape
@param shape the point feature that will be matched
@param ilev one certain pyramid level
@param image the image resource
@param lbp_img the LBP-operator image
@param norm the \f$L2\f$-norm of the difference between \a shape and \a update_shape
@return how many point vertex will be updated?
*/
int MatchToModel(asm_shape& update_shape, const asm_shape& shape,
int ilev, const IplImage* image, const int *lbp_img, double* norm = NULL);

/**
Calculate shape parameters (\a a, \a b, \a Tx, \a Ty) and pose parameters \a p.
@param p Shape parameters
@param a \f$ s \times cos(theta) \f$ in form of similarity transform
@param b \f$ s \times sin(theta) \f$ in form of similarity transform
@param tx \f$ Tx \f$ in form of similarity transform
@param ty \f$ Ty \f$ in form of similarity transform
@param shape the point features data
@param iter_no Number of iteration
*/
void CalcParams(CvMat* p, double& a, double& b, double& tx, double& ty,
const asm_shape& shape, int iter_no = 2);

/**
Constrain the shape parameters.
@param p Shape parameters
*/
void Clamp(CvMat* p);

/**
Generate shape instance according to shape parameters p and pose parameters.
@param shape the point feature data
@param p the shape parameters
@param a Return \f$ s \times cos(theta) \f$ in form of similarity transform
@param b Return \f$ s \times sin(theta) \f$ in form of similarity transform
@param tx Return \f$ Tx \f$ in form of similarity transform
@param ty Return \f$ Ty \f$ in form of similarity transform
*/
void CalcGlobalShape(asm_shape& shape, const CvMat* p,
double a, double b, double tx, double ty);

/**
Pyramid fitting at one certain level.
@param shape the point feature data
@param image the image resource
@param ilev one certain pyramid level
@param iter_no the number of iteration
*/
void PyramidFit(asm_shape& shape, const IplImage* image, int ilev, int iter_no);

private:

CvMat* m_M; /**< mean vector of shape data */
CvMat* m_B; /**< eigenvetors of shape data */
CvMat* m_V; /**< eigenvalues of shape data */

CvMat* m_SM; /**< mean of shapes projected space */
CvMat* m_SSD; /**< standard deviation of shapes projected space */


ASM_PROFILE_TYPE m_type; /**< the type of sampling profile */

/**< the profile distribution model */
union
{
struct profile_lbp_model* lbp_tdm; /**< lbp profile model */
struct profile_Nd_model* classical_tdm; /**< 1d/2d profile model */
};


asm_shape m_asm_meanshape; /**< mean shape of aligned shapes */

int m_nPoints; /**< number of shape points */
int m_nWidth; /**< width of each landmark's profile */
int m_nLevels; /**< pyramid level of multi-resolution */
int m_nModes; /**< number of truncated eigenvalues */
double m_dReferenceFaceWidth; /**< width of reference face */
bool m_bInterpolate; /**< whether to using image interpolate or not*/
double m_dMeanCost; /**< the mean of fitting cost to determine whether fitting succeed or not*/
double m_dVarCost; /**< the variance of fitting cost determine whether fitting succeed or not*/

private:
CvMat* m_CBackproject; /**< Cached variables for speed up */
CvMat* m_CBs; /**< Cached variables for speed up */
double* m_dist; /**< Cached variables for speed up */
asm_profile* m_profile; /**< Cached variables for speed up */
asm_shape m_search_shape; /**< Cached variables for speed up */
asm_shape m_temp_shape; /**< Cached variables for speed up */
};

/** You can define your own face detector function here
@param shapes Returned face detected box which stores the Top-Left and Bottom-Right points, so its \a NPoints() = 2 here.
@param image Image resource.
@return false on no face exists in image, true otherwise.
*/
typedef bool (*detect_func)(asm_shape& shape, const IplImage* image);

#endif // _ASM_LIBRARY_H_

(5)DemoFit.cpp

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/contrib/detection_based_tracker.hpp>

#include "asmfitting.h"
#include "vjfacedetect.h"

#include <string>
#include <vector>

#include <android/log.h>
#include <jni.h>

using namespace std;
using namespace cv;

#define LOG_TAG "ASMLIBRARY"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

#define BEGINT() double t = (double)cvGetTickCount();
#define ENDT(exp) t = ((double)cvGetTickCount() - t )/ (cvGetTickFrequency()*1000.); \
LOGD(exp " time cost: %.2f millisec\n", t);

asmfitting fit_asm;
DetectionBasedTracker *track = NULL;

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeReadModel
(JNIEnv * jenv, jclass, jstring jFileName)
{
LOGD("nativeReadModel enter");
const char* filename = jenv->GetStringUTFChars(jFileName, NULL);
jboolean result = false;

try
{
if(fit_asm.Read(filename) == true)
result = true;

}
catch (...)
{
LOGD("nativeReadModel caught unknown exception");
jclass je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, "Unknown exception in JNI code");
}

LOGD("nativeReadModel %s exit %d", filename, result);
return result;
}

JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeInitCascadeDetector
(JNIEnv * jenv, jclass, jstring jFileName)
{
const char* cascade_name = jenv->GetStringUTFChars(jFileName, NULL);
LOGD("nativeInitCascadeDetector %s enter", cascade_name);

if(init_detect_cascade(cascade_name) == false)
return false;

LOGD("nativeInitCascadeDetector exit");
return true;
}

JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeInitFastCascadeDetector
(JNIEnv * jenv, jclass, jstring jFileName)
{
const char* cascade_name = jenv->GetStringUTFChars(jFileName, NULL);
LOGD("nativeInitFastCascadeDetector %s enter", cascade_name);

DetectionBasedTracker::Parameters DetectorParams;
DetectorParams.minObjectSize = 45;
track = new DetectionBasedTracker(cascade_name, DetectorParams);

if(track == NULL) return false;

DetectorParams = track->getParameters();
DetectorParams.minObjectSize = 64;
track->setParameters(DetectorParams);

track->run();

LOGD("nativeInitFastCascadeDetector exit");
return true;
}

JNIEXPORT void JNICALL Java_org_asmlibrary_fit_ASMFit_nativeDestroyCascadeDetector
(JNIEnv * jenv, jclass)
{
LOGD("nativeDestroyCascadeDetector enter");

destory_detect_cascade();

LOGD("nativeDestroyCascadeDetector exit");
}

JNIEXPORT void JNICALL Java_org_asmlibrary_fit_ASMFit_nativeDestroyFastCascadeDetector
(JNIEnv * jenv, jclass)
{
LOGD("nativeDestroyFastCascadeDetector enter");

if(track){
track->stop();
delete track;
}

LOGD("nativeDestroyFastCascadeDetector exit");
}

inline void shape_to_Mat(asm_shape shapes[], int nShape, Mat& mat)
{
mat = Mat(nShape, shapes[0].NPoints()*2, CV_64FC1);

for(int i = 0; i < nShape; i++)
{
double *pt = mat.ptr<double>(i);
for(int j = 0; j < mat.cols/2; j++)
{
pt[2*j] = shapes[i][j].x;
pt[2*j+1] = shapes[i][j].y;
}
}
}

inline void Mat_to_shape(asm_shape shapes[], int nShape, Mat& mat)
{
for(int i = 0; i < nShape; i++)
{
double *pt = mat.ptr<double>(i);
shapes[i].Resize(mat.cols/2);
for(int j = 0; j < mat.cols/2; j++)
{
shapes[i][j].x = pt[2*j];
shapes[i][j].y = pt[2*j+1];
}
}
}

JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeFastDetectAll
(JNIEnv * jenv, jclass, jlong imageGray, jlong faces)
{
if(!track) return false;

BEGINT();

vector<Rect> RectFaces;
try{
Mat image = *(Mat*)imageGray;
LOGD("image: (%d, %d)", image.cols, image.rows);
track->process(image);
track->getObjects(RectFaces);
}
catch(cv::Exception& e)
{
LOGD("nativeFastDetectAll caught cv::Exception: %s", e.what());
jclass je = jenv->FindClass("org/opencv/core/CvException");
if(!je)
je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, e.what());
}
catch (...)
{
LOGD("nativeFastDetectAll caught unknown exception");
jclass je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, "Unknown exception in JNI code");
}

int nFaces = RectFaces.size();
if(nFaces <= 0){
ENDT("FastCascadeDetector CANNOT detect any face");
return false;
}

LOGD("FastCascadeDetector found %d faces", nFaces);

asm_shape* detshapes = new asm_shape[nFaces];
for(int i = 0; i < nFaces; i++){
Rect r = RectFaces[i];
detshapes[i].Resize(2);
detshapes[i][0].x = r.x;
detshapes[i][0].y = r.y;
detshapes[i][1].x = r.x+r.width;
detshapes[i][1].y = r.y+r.height;
}

asm_shape* shapes = new asm_shape[nFaces];
for(int i = 0; i < nFaces; i++)
{
InitShapeFromDetBox(shapes[i], detshapes[i], fit_asm.GetMappingDetShape(), fit_asm.GetMeanFaceWidth());
}

shape_to_Mat(shapes, nFaces, *((Mat*)faces));

delete []detshapes;
delete []shapes;

ENDT("FastCascadeDetector detect");

return true;
}

JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeDetectAll
(JNIEnv * jenv, jclass, jlong imageGray, jlong faces)
{
IplImage image = *(Mat*)imageGray;
int nFaces;
asm_shape *detshapes = NULL;

LOGD("image: (%d, %d)", image.width, image.height);

BEGINT();

bool flag =detect_all_faces(&detshapes, nFaces, &image);
if(flag == false) {
ENDT("CascadeDetector CANNOT detect any face");
return false;
}

LOGD("CascadeDetector found %d faces", nFaces);
asm_shape* shapes = new asm_shape[nFaces];
for(int i = 0; i < nFaces; i++)
{
InitShapeFromDetBox(shapes[i], detshapes[i], fit_asm.GetMappingDetShape(), fit_asm.GetMeanFaceWidth());
}

shape_to_Mat(shapes, nFaces, *((Mat*)faces));
free_shape_memeory(&detshapes);
delete []shapes;

ENDT("CascadeDetector detect");

return true;
}

JNIEXPORT void JNICALL Java_org_asmlibrary_fit_ASMFit_nativeInitShape(JNIEnv * jenv, jclass, jlong faces)
{
Mat faces1 = *((Mat*)faces);
int nFaces = faces1.rows;
asm_shape* detshapes = new asm_shape[nFaces];
asm_shape* shapes = new asm_shape[nFaces];

Mat_to_shape(detshapes, nFaces, faces1);

for(int i = 0; i < nFaces; i++)
{
InitShapeFromDetBox(shapes[i], detshapes[i], fit_asm.GetMappingDetShape(), fit_asm.GetMeanFaceWidth());
}

shape_to_Mat(shapes, nFaces, *((Mat*)faces));

delete []detshapes;
delete []shapes;
}


JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeDetectOne
(JNIEnv * jenv, jclass, jlong imageGray, jlong faces)
{
IplImage image = *(Mat*)imageGray;
asm_shape shape, detshape;

BEGINT();

bool flag = detect_one_face(detshape, &image);

if(flag == false) {
ENDT("CascadeDetector CANNOT detect any face");
return false;
}

InitShapeFromDetBox(shape, detshape, fit_asm.GetMappingDetShape(), fit_asm.GetMeanFaceWidth());

shape_to_Mat(&shape, 1, *((Mat*)faces));

ENDT("CascadeDetector detects central face");

return true;
}


JNIEXPORT void JNICALL Java_org_asmlibrary_fit_ASMFit_nativeFitting
(JNIEnv * jenv, jclass, jlong imageGray, jlong shapes0)
{
IplImage image = *(Mat*)imageGray;
Mat shapes1 = *(Mat*)shapes0;
int nFaces = shapes1.rows;
asm_shape* shapes = new asm_shape[nFaces];

BEGINT();

Mat_to_shape(shapes, nFaces, shapes1);

fit_asm.Fitting2(shapes, nFaces, &image);

shape_to_Mat(shapes, nFaces, *((Mat*)shapes0));

ENDT("nativeFitting");

//for(int i = 0; i < shapes[0].NPoints(); i++)
// LOGD("points: (%f, %f)", shapes[0][i].x, shapes[0][i].y);

delete []shapes;
}

JNIEXPORT jboolean JNICALL Java_org_asmlibrary_fit_ASMFit_nativeVideoFitting
(JNIEnv * jenv, jclass, jlong imageGray, jlong shapes0, jlong frame)
{
IplImage image = *(Mat*)imageGray;
Mat shapes1 = *(Mat*)shapes0;
bool flag = false;
if(shapes1.rows == 1)
{
asm_shape shape;

LOGD("nativeVideoFitting %d x %d", image.width, image.height);
BEGINT();

Mat_to_shape(&shape, 1, shapes1);

flag = fit_asm.ASMSeqSearch(shape, &image, frame, true);

shape_to_Mat(&shape, 1, *((Mat*)shapes0));

ENDT("nativeVideoFitting");
}

return flag;
}

#ifdef __cplusplus
}
#endif

(6)vjfacedetect.h

#ifndef _VJFACE_DETECT_H_
#define _VJFACE_DETECT_H_

#include "asmlibrary.h"


/**
Load adaboost cascade file for detect face.
@param cascade_name Filename the cascade detector located in
@return false on failure, true otherwise
*/
bool init_detect_cascade(const char* cascade_name = "haarcascade_frontalface_alt2.xml");


/**
Release the memory of adaboost cascade face detector
*/
void destory_detect_cascade();

/**
Detect only one face from image, and this human face is located as close as to the center of image
@param shape return face detected box which stores the Top-Left and Bottom-Right points, so its \a NPoints() = 2 here
@param image the image resource
@return false on no face exists in image, true otherwise
*/
bool detect_one_face(asm_shape& shape, const IplImage* image);


/**
Detect all human face from image.
@param shapes return face detected box which stores the Top-Left and Bottom-Right points, so its \a NPoints() = 2 here
@param n_shapes the numbers of faces to return
@param image the image resource
@return false on no face exists in image, true otherwise
*/
bool detect_all_faces(asm_shape** shapes, int& n_shapes, const IplImage* image);

/**
Release the shape resource allocated by detect_all_faces().
@param shapes the ptr of asm_shape []
*/
void free_shape_memeory(asm_shape** shapes);



#endif // _VJFACE_DETECT_H_


未完待续,我发现这样去看代码,有点看不懂