文档视界 最新最全的文档下载
当前位置:文档视界 › OpenCV学习笔记A

OpenCV学习笔记A

OpenCV学习笔记A
OpenCV学习笔记A

分类器(模式识别)

目标检测方法最初由Paul Viola [Viola01]提出,并由Rainer Lienhart [Lienhart02]对这一方法进行了改善. 首先,利用样本(大约几百幅样本图片)的 harr 特征进行分类器训练,得到一个级联的boosted分类器。训练样本分为正例样本和反例样本,其中正例样本是指待检目标样本(例如人脸或汽车等),反例样本指其它任意图片,所有的样本图片都被归一化为同样的尺寸大小(例如,

20x20)。

分类器训练完以后,就可以应用于输入图像中的感兴趣区域(与训练样本相同的尺寸)的检测。检测到目标区域(汽车或人脸)分类器输出为1,否则输出为0。为了检测整副图像,可以在图像中移动搜索窗口,检测每一个位置来确定可能的目标。为了搜索不同大小的目标物体,分类器被设计为可以进行尺寸改变,这样比改变待检图像的尺寸大小更为有效。所以,为了在图像中检测未知大小的目标物体,扫描程序通常需要用不同比例大小的搜索窗口对图片进行几次扫描。

分类器中的“级联”是指最终的分类器是由几个简单分类器级联组成。在图像检测中,被检窗口依次通过每一级分类器,这样在前面几层的检测中大部分的候选区域就被排除了,全部通过每一级分类器检测的区域即为目标区域。目前支持这种分类器的boosting技术有四种: Discrete Adaboost, Real Adaboost, Gentle Adaboost and Logitboost。"boosted" 即指级联分类器的每一层都可以从中选取一个boosting算法(权重投票),并利用基础分类器的自我训练得到。基础分类器是至少有两个叶结点的决策树分类器。 Haar特征是基础分类器的输入,主要描述如下。目前的算法主要利用下面的Harr特征。

每个特定分类器所使用的特征用形状、感兴趣区域中的位置以及比例系数(这里的比例系数跟检测时候采用的比例系数是不一样的,尽管最后会取两个系数的乘积值)来定义。例如在第三行特征

(2c)的情况下,响应计算为复盖全部特征整个矩形框(包括两个白色矩形框和一个黑色矩形框)象素的和减去黑色矩形框内象素和的三倍。每个矩形框内的象素和都可以通过积分图象很快的计算出来。(察看下面和对cvIntegral的描述).

通过HaarFaceDetect 的演示版可以察看目标检测的工作情况。

下面只是检测部分的参考手册。 haartraining是它的一个单独的应用,可以用来对系列样本训练级联的 boosted分类器。详细察看opencv/apps/haartraining。

CvHaarFeature, CvHaarClassifier, CvHaarStageClassifier, CvHaarClassifierCascade Boosted Haar 分类器结构

#define CV_HAAR_FEATURE_MAX 3

/* 一个 harr 特征由 2-3 个具有相应权重的矩形组成a haar feature consists of 2-3

rectangles with appropriate weights */

typedef struct CvHaarFeature

{

int tilted; /* 0 means up-right feature, 1 means 45--rotated feature */

/* 2-3 rectangles with weights of opposite signs and

with absolute values inversely proportional to the areas of the rectangles.

if rect[2].weight !=0, then

the feature consists of 3 rectangles, otherwise it consists of 2 */

struct

{

CvRect r;

float weight;

} rect[CV_HAAR_FEATURE_MAX];

}

CvHaarFeature;

/* a single tree classifier (stump in the simplest case) that returns the response for

the feature

at the particular image location (i.e. pixel sum over subrectangles of the window)

and gives out

a value depending on the responce */

typedef struct CvHaarClassifier

{

int count; /* number of nodes in the decision tree */

/* these are "parallel" arrays. Every index i

corresponds to a node of the decision tree (root has 0-th index).

left[i] - index of the left child (or negated index if the left child is a leaf) right[i] - index of the right child (or negated index if the right child is a

leaf)

threshold[i] - branch threshold. if feature responce is <= threshold, left

branch

is chosen, otherwise right branch is chosed.

alpha[i] - output value correponding to the leaf. */

CvHaarFeature* haar_feature;

float* threshold;

int* left;

int* right;

float* alpha;

}

CvHaarClassifier;

/* a boosted battery of classifiers(=stage classifier):

the stage classifier returns 1

if the sum of the classifiers' responces

is greater than threshold and 0 otherwise */

typedef struct CvHaarStageClassifier

{

int count; /* number of classifiers in the battery */

float threshold; /* threshold for the boosted classifier */

CvHaarClassifier* classifier; /* array of classifiers */

/* these fields are used for organizing trees of stage classifiers,

rather than just stright cascades */

int next;

int child;

int parent;

}

CvHaarStageClassifier;

typedef struct CvHidHaarClassifierCascade CvHidHaarClassifierCascade;

/* cascade or tree of stage classifiers */

typedef struct CvHaarClassifierCascade

{

int flags; /* signature */

int count; /* number of stages */

CvSize orig_window_size; /* original object size (the cascade is trained for) */

/* these two parameters are set by cvSetImagesForHaarClassifierCascade */

CvSize real_window_size; /* current object size */

double scale; /* current scale */

CvHaarStageClassifier* stage_classifier; /* array of stage classifiers */

CvHidHaarClassifierCascade* hid_cascade; /* hidden optimized representation of the

cascade,

created by

cvSetImagesForHaarClassifierCascade */

}

CvHaarClassifierCascade;

所有的结构都代表一个级联boosted Haar分类器。级联有下面的等级结构:

Cascade:

Stage1:

Classifier11:

Feature11

Classifier12:

Feature12

...

Stage2:

Classifier21:

Feature21

...

...

整个等级可以手工构建,也可以利用函数cvLoadHaarClassifierCascade从已有的磁盘文件或嵌入式基中导入。

cvLoadHaarClassifierCascade

从文件中装载训练好的级联分类器或者从OpenCV中嵌入的分类器数据库中导入

CvHaarClassifierCascade* cvLoadHaarClassifierCascade(

const char* directory,

CvSize orig_window_size );

directory

训练好的级联分类器的路径

orig_window_size

级联分类器训练中采用的检测目标的尺寸。因为这个信息没有在级联分类器中存储,所有要单独指

出。

函数 cvLoadHaarClassifierCascade 用于从文件中装载训练好的利用海尔特征的级联分类器,或者从OpenCV中嵌入的分类器数据库中导入。分类器的训练可以应用函数haartraining(详细察看

opencv/apps/haartraining) 这个数值是在训练分类器时就确定好的,修改它并不能改变检测的范

围或精度。

需要注意的是,这个函数已经过时了。现在的目标检测分类器通常存储在 XML 或 YAML 文件中,而不是通过路径导入。从文件中导入分类器,可以使用函数 cvLoad 。

cvReleaseHaarClassifierCascade

释放haar classifier cascade。

void cvReleaseHaarClassifierCascade( CvHaarClassifierCascade** cascade );

cascade

双指针类型指针指向要释放的cascade. 指针由函数声明。

函数 cvReleaseHaarClassifierCascade 释放cascade的动态内存,其中cascade的动态内存或者是手工创建,或者通过函数 cvLoadHaarClassifierCascade 或 cvLoad分配。

cvHaarDetectObjects

检测图像中的目标

typedef struct CvAvgComp

{

CvRect rect; /* bounding rectangle for the object (average rectangle of a group) */ int neighbors; /* number of neighbor rectangles in the group */

}

CvAvgComp;

CvSeq* cvHaarDetectObjects( const CvArr* image, CvHaarClassifierCascade* cascade,

CvMemStorage* storage, double scale_factor=1.1,

int min_neighbors=3, int flags=0,

CvSize min_size=cvSize(0,0) );

image

被检图像

cascade

harr 分类器级联的内部标识形式

storage

用来存储检测到的一序列候选目标矩形框的内存区域。

scale_factor

在前后两次相继的扫描中,搜索窗口的比例系数。例如1.1指将搜索窗口依次扩大10%。

min_neighbors

构成检测目标的相邻矩形的最小个数(缺省-1)。如果组成检测目标的小矩形的个数和小于

min_neighbors-1 都会被排除。如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检

候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。

flags

操作方式。当前唯一可以定义的操作方式是 CV_HAAR_DO_CANNY_PRUNING。如果被设定,函数利用

Canny边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域一般不含被检目标。人

脸检测中通过设定阈值使用了这种方法,并因此提高了检测速度。

min_size

检测窗口的最小尺寸。缺省的情况下被设为分类器训练时采用的样本尺寸(人脸检测中缺省大小是

~20×20)。

函数 cvHaarDetectObjects 使用针对某目标物体训练的级联分类器在图像中找到包含目标物体的矩形区域,并且将这些区域作为一序列的矩形框返回。函数以不同比例大小的扫描窗口对图像进行几次搜索(察看cvSetImagesForHaarClassifierCascade)。每次都要对图像中的这些重叠区域利用cvRunHaarClassifierCascade进行检测。有时候也会利用某些继承(heuristics)技术以减少分析的候选区域,例如利用 Canny 裁减(prunning)方法。函数在处理和收集到候选的方框(全部通过级联分类器各层的区域)之后,接着对这些区域进行组合并且返回一系列各个足够大的组合中的平均矩形。调节程序中的缺省参数(scale_factor=1.1, min_neighbors=3, flags=0)用于对目标进行更精确同时也是耗时较长的进一步检测。为了能对视频图像进行更快的实时检测,参数设置通常是:scale_factor=1.2, min_neighbors=2, flags=CV_HAAR_DO_CANNY_PRUNING,

min_size= (例如, 对于视频会议的图像区域).

例子:利用级联的Haar classifiers寻找检测目标(e.g. faces).

#include "cv.h"

#include "highgui.h"

CvHaarClassifierCascade* load_object_detector( const char* cascade_path )

{

return (CvHaarClassifierCascade*)cvLoad( cascade_path );

}

void detect_and_draw_objects( IplImage* image,

CvHaarClassifierCascade* cascade,

int do_pyramids )

{

IplImage* small_image = image;

CvMemStorage* storage = cvCreateMemStorage(0);

CvSeq* faces;

int i, scale = 1;

/* if the flag is specified, down-scale the 输入图像 to get a

performance boost w/o loosing quality (perhaps) */

if( do_pyramids )

{

small_image = cvCreateImage( cvSize(image->width/2,image->height/2),

IPL_DEPTH_8U, 3 );

cvPyrDown( image, small_image, CV_GAUSSIAN_5x5 );

scale = 2;

}

/* use the fastest variant */

faces = cvHaarDetectObjects( small_image, cascade, storage, 1.2, 2,

CV_HAAR_DO_CANNY_PRUNING );

/* draw all the rectangles */

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

{

/* extract the rectanlges only */

CvRect face_rect = *(CvRect*)cvGetSeqElem( faces, i, 0 );

cvRectangle( image, cvPoint(face_rect.x*scale,face_rect.y*scale),

cvPoint((face_rect.x+face_rect.width)*scale,

(face_rect.y+face_rect.height)*scale),

CV_RGB(255,0,0), 3 );

}

if( small_image != image )

cvReleaseImage( &small_image );

cvReleaseMemStorage( &storage );

}

/* takes image filename and cascade path from the command line */

int main( int argc, char** argv )

{

IplImage* image;

if( argc==3 && (image = cvLoadImage( argv[1], 1 )) != 0 )

{

CvHaarClassifierCascade* cascade = load_object_detector(argv[2]);

detect_and_draw_objects( image, cascade, 1 );

cvNamedWindow( "test", 0 );

cvShowImage( "test", image );

cvWaitKey(0);

cvReleaseHaarClassifierCascade( &cascade );

cvReleaseImage( &image );

}

return 0;

}

cvSetImagesForHaarClassifierCascade

为隐藏的cascade(hidden cascade)指定图像

void cvSetImagesForHaarClassifierCascade( CvHaarClassifierCascade* cascade,

const CvArr* sum, const CvArr* sqsum,

const CvArr* tilted_sum, double scale ); cascade

隐藏 Harr 分类器级联(Hidden Haar classifier cascade), 由函数

cvCreateHidHaarClassifierCascade生成

sum

32-比特,单通道图像的积分图像(Integral (sum) 单通道 image of 32-比特 integer format)

. 这幅图像以及随后的两幅用于对快速特征的评价和亮度/对比度的归一化。它们都可以利用函数

cvIntegral从8-比特或浮点数单通道的输入图像中得到。

sqsum

单通道64比特图像的平方和图像

tilted_sum

单通道32比特整数格式的图像的倾斜和(Tilted sum)

scale

cascade的窗口比例. 如果 scale=1, 就只用原始窗口尺寸检测 (只检测同样尺寸大小的目标物体) - 原始窗口尺寸在函数cvLoadHaarClassifierCascade中定义 (在 ""中缺

省为24x24), 如果scale=2, 使用的窗口是上面的两倍 (在face cascade中缺省值是48x48 )。这样

尽管可以将检测速度提高四倍,但同时尺寸小于48x48的人脸将不能被检测到。

函数 cvSetImagesForHaarClassifierCascade 为hidden classifier cascade 指定图像 and/or 窗口比例系数。如果图像指针为空,会继续使用原来的图像(i.e. NULLs 意味这"不改变图像")。比

例系数没有 "protection" 值,但是原来的值可以通过函数 cvGetHaarClassifierCascadeScale 重

新得到并使用。这个函数用于对特定图像中检测特定目标尺寸的cascade分类器的设定。函数通过cvHaarDetectObjects进行内部调用,但当需要在更低一层的函数cvRunHaarClassifierCascade中使用的时候,用户也可以自行调用。

cvRunHaarClassifierCascade

在给定位置的图像中运行 cascade of boosted classifier

int cvRunHaarClassifierCascade( CvHaarClassifierCascade* cascade,

CvPoint pt, int start_stage=0 );

cascade

Haar 级联分类器

pt

待检测区域的左上角坐标。待检测区域大小为原始窗口尺寸乘以当前设定的比例系数。当前窗口尺

寸可以通过cvGetHaarClassifierCascadeWindowSize重新得到。

start_stage

级联层的初始下标值(从0开始计数)。函数假定前面所有每层的分类器都已通过。这个特征通过函

数cvHaarDetectObjects内部调用,用于更好的处理器高速缓冲存储器。

函数 cvRunHaarHaarClassifierCascade 用于对单幅图片的检测。在函数调用前首先利用cvSetImagesForHaarClassifierCascade设定积分图和合适的比例系数 (=> 窗口尺寸)。当分析的矩

形框全部通过级联分类器每一层的时返回正值(这是一个候选目标),否则返回0或负值。

分类器模型1:朴素贝叶斯分类器

贝叶斯分类器的分类原理是通过某对象的先验概率,利用贝叶斯公式计算出其后验概率,即该对象属于某一类的概率,选择具有最大后验概率的类作为该对象所属的类。目前研究较多的贝叶斯分类器主要有四种,分别是:Naive Bayes、TAN、BAN和GBN。

贝叶斯网络是一个带有概率注释的有向无环图,图中的每一个结点均表示一个随机变量,图中两结点间若存在着一条弧,则表示这两结点相对应的随机变量是概率相依的,反之则说明这两个随机变量是条件独立的。网络中任意一个结点X 均有一个相应的条件概率表(Conditional Probability Table,CPT),用以表示结点X 在其父结点取各可能值时的条件概率。若结点X 无父结点,则X 的CPT 为其先验概率分布。贝叶斯网络的结构及各结点的CPT 定义了网络中各变量的概率分布。

贝叶斯分类器是用于分类的贝叶斯网络。该网络中应包含类结点C,其中C 的取值来自于类集合( c1 , c2 , ... , cm),还包含一组结点X = ( X1 , X2 , ... , Xn),表示用于分类的特征。对于贝叶斯网络分类器,若某一待分类的样本D,其分类特征值为x = ( x1 , x2 , ... , x n) ,则样本D 属于类别ci 的概率P( C = ci | X1 = x1 , X2 = x 2 , ... , Xn = x n) ,( i = 1 ,2 , ... , m) 应满足下式:

P( C = ci | X = x) = Max{ P( C = c1 | X = x) , P( C = c2 | X = x ) , ... , P( C = cm | X = x ) }

而由贝叶斯公式:

P( C = ci | X = x) = P( X = x | C = ci) * P( C = ci) / P( X = x)

其中,P( C = ci) 可由领域专家的经验得到,而P( X = x | C = ci) 和P( X = x) 的计算则较困难。

应用贝叶斯网络分类器进行分类主要分成两阶段。第一阶段是贝叶斯网络分类器的学习,即从样本数据中构造分类器,包括结构学习和CPT 学习;第二阶段是贝叶斯网络分类器的推理,即计算类结点的条件概率,对分类数据进行分类。这两个阶段的时间复杂性均取决于特征值间的依赖程度,甚至可以是NP 完

全问题,因而在实际应用中,往往需要对贝叶斯网络分类器进行简化。根据对特征值间不同关联程度的假设,可以得出各种贝叶斯分类器,Naive Bayes、TAN、BAN、GBN 就是其中较典型、研究较深入的贝叶斯分类器。

朴素贝叶斯

分类是将一个未知样本分到几个预先已知类的过程。数据分类问题的解决是一个两步过程:第一步,

建立一个模型,描述预先的数据集或概念集。通过分析由属性描述的样本(或实例,对象等)来构造模型。假定每一个样本都有一个预先定义的类,由一个被称为类标签的属性确定。为建立模型而被分析的数据元组形成训练数据集,该步也称作有指导的学习。

在众多的分类模型中,应用最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBC)。决策树模型通过构造树来解决分类问题。首先利用训练数据集来构造一棵决策树,一旦树建立起来,它就可为未知样本产生一个分类。在分类问题中使用决策树模型有很多的优点,决策树便于使用,而且高效;根据决策树可以很容易地构造出规则,而规则通常易于解释和理解;决策树可很好地扩展到大型数据库中,同时它的大小独立于数据库的大小;决策树模型的另外一大优点就是可以对有许多属性的数据集构造决策树。决策树模型也有一些缺点,比如处理缺失数据时的困难,过度拟合问题的出现,以及忽略数据集中属性之间的相关性等。

和决策树模型相比,朴素贝叶斯模型发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC 模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给 NBC模型的正确分类带来了一定影响。在属性个数比较多或者属性之间相关性较大时,NBC模型的分类效率比不上决策树模型。而在属性相关性较小时,NBC 模型的性能最为良好。

朴素贝叶斯模型:

----

Vmap=arg max P( Vj | a1,a2...an)

Vj属于V集合

其中Vmap是给定一个example,得到的最可能的目标值.

其中a1...an是这个example里面的属性.

这里面,Vmap目标值,就是后面计算得出的概率最大的一个.所以用max 来表示

----

贝叶斯公式应用到 P( Vj | a1,a2...an)中.

可得到 Vmap= arg max P(a1,a2...an | Vj ) P( Vj ) / P (a1,a2...an)

又因为朴素贝叶斯分类器默认a1...an他们互相独立的.

所以P(a1,a2...an)对于结果没有用处. [因为所有的概率都要除同一个东西之后再比较大小,最后结果也似乎影响不大]

可得到Vmap= arg max P(a1,a2...an | Vj ) P( Vj )

然后

"朴素贝叶斯分类器基于一个简单的假定:给定目标值时属性之间相互条件独立。换言之。该假定说明给定实力的目标值情况下。观察到联合的a1,a2...an的概率正好是对每个单独属性的概率乘积:

P(a1,a2...an | Vj ) = Π i P( ai| Vj )

....

朴素贝叶斯分类器:Vnb =arg max P( Vj ) Π i P ( ai | Vj )

"

Vnb = arg max P ( Vj )

此处Vj ( yes | no ),对应天气的例子。

备注:贝叶斯相关理论

【理论概述】

贝叶斯(1702-1763) Thomas Bayes,英国数学家.1702年出生于伦敦,做过神甫。1742年成为英国皇家学会会员。1763年4月7日逝世。贝叶斯在数学方面主要研究概率论。他首先将归纳推理法用于概率论基础理论,并创立了贝叶斯统计理论,对于统计决策函数、统计推断、统计的估算等做出了贡献.1763年发表了这方面的论著,对于现代概率论和数理统计都有很重要的作用。贝叶斯的另一著作《机会的学说概论》发表于1758年。贝叶斯所采用的许多术语被沿用至今。

贝叶斯决策理论是主观贝叶斯派归纳理论的重要组成部分。

贝叶斯决策就是在不完全情报下,对部分未知的状态用主观概率估计,然后用贝叶斯公式对发生概率进行修正,最后再利用期望值和修正概率做出最优决策。

贝叶斯决策理论方法是统计模型决策中的一个基本方法,其基本思想是:

1、已知类条件概率密度参数表达式和先验概率。

2、利用贝叶斯公式转换成后验概率。

3、根据后验概率大小进行决策分类。

他对统计推理的主要贡献是使用了"逆概率"这个概念,并把它作为一种普遍的推理方法提出来。贝叶斯定理原本是概率论中的一个定理,这一定理可用一个数学公式来表达,这个公式就是著名的贝叶斯公式。贝叶斯公式是他在1763年提出来的:

假定B1,B2,……是某个过程的若干可能的前提,则P(Bi)是人们事先对各前提条件出现可能性大小的估计,称之为先验概率。如果这个过程得到了一个结果A,那么贝叶斯公式提供了我们根据A的出现而对前提条件做出新评价的方法。P(Bi∣A)既是对以A为前提下Bi的出现概率的重新认识,称P(Bi∣A)为后验概率。经过多年的发展与完善,贝叶斯公式以及由此发展起来的一整套理论与方法,已经成为概率统计中的一个冠以“贝叶斯”名字的学派,在自然科学及国民经济的许多领域中有着广泛应用。

【贝叶斯公式】

设D1,D2,……,Dn为样本空间S的一个划分,如果以P(Di)表示事件Di发生的概率,且P(Di)>0(i=1,2,…,n)。对于任一事件x,P(x)>0,则有:

n

P(Dj/x)=p(x/Dj)P(Dj)/∑P(X/Di)P(Di)

i=1

贝叶斯公式

【贝叶斯决策理论分析】

(1)如果我们已知被分类类别概率分布的形式和已经标记类别的训练样本集合,那我们就需要从训练样本集合中来估计概率分布的参数。在现实世界中有时会出现这种情况。(如已知为正态分布了,根据标记好类别的样本来估计参数,常见的是极大似然率和贝叶斯参数估计方法)

(2)如果我们不知道任何有关被分类类别概率分布的知识,已知已经标记类别的训练样本集合和判别式函数的形式,那我们就需要从训练样本集合中来估计判别式函数的参数。在现实世界中有时会出现这种情况。(如已知判别式函数为线性或二次的,那么就要根据训练样本来估计判别式的参数,常见的是线性判别式和神经网络)

(3)如果我们既不知道任何有关被分类类别概率分布的知识,也不知道判别式函数的形式,只有已经标记类别的训练样本集合。那我们就需要从训练样本集合中来估计概率分布函数的参数。在现实世界中经常出现这种情况。(如首先要估计是什么分布,再估计参数。常见的是非参数估计)

(4)只有没有标记类别的训练样本集合。这是经常发生的情形。我们需要对训练样本集合进行聚类,从而估计它们概率分布的参数。(这是无监督的学习)

(5)如果我们已知被分类类别的概率分布,那么,我们不需要训练样本集合,利用贝叶斯决策理论就可以设计最优分类器。但是,在现实世界中从没有出现过这种情况。这里是贝叶斯决策理论常用的地方。

问题:假设我们将根据特征矢量x 提供的证据来分类某个物体,那么我们进行分类的标准是什么?decide wj, if(p(wj|x)>p(wi|x))(i不等于j)应用贝叶斯展开后可以得到p(x|wj)p(wj)>p(x|wi)p(wi)即或然率p(x|wj)/p(x|wi)>p(wi)/p(wj),决策规则就是似然率测试规则。

结论:对于任何给定问题,可以通过似然率测试决策规则得到最小的错误概率。这个错误概率称为贝叶斯错误率,且是所有分类器中可以得到的最好结果。最小化错误概率的决策规则就是最大化后验概率判据。

【贝叶斯决策判据】

贝叶斯决策理论方法是统计模式识别中的一个基本方法。贝叶斯决策判据既考虑了各类参考总体出现的概率大小,又考虑了因误判造成的损失大小,判别能力强。贝叶斯方法更适用于下列场合:

(1) 样本(子样)的数量(容量)不充分大,因而大子样统计理论不适宜的场合。

(2) 试验具有继承性,反映在统计学上就是要具有在试验之前已有先验信息的场合。用这种方法进行分类时要求两点:第一,要决策分类的参考总体的类别数是一定的。例如两类参考总体(正常状态Dl和异常状态D2),或L类参考总体D1,D2,…,DL(如良好、满意、可以、不满意、不允许、……)。

第二,各类参考总体的概率分布是已知的,即每一类参考总体出现的先验概率P(Di)以及各类概率密度函数P(x/Di)是已知的。显然,0≤P(Di)≤1,(i=l,2,…,L),∑P(Di)=1。

对于两类故障诊断问题,就相当于在识别前已知正常状态D1的概率户(D1)和异常状态0:的概率

P(D2),它们是由先验知识确定的状态先验概率。如果不做进一步的仔细观测,仅依靠先验概率去作决策,那么就应给出下列的决策规则:若 P(D1)>P(D2),则做出状态属于D1类的决策;反之,则做出状态属于D2类的决策。例如,某设备在365天中,有故障是少见的,无故障是经常的,有故障的概率远小于无故障的概率。因此,若无特B,j明显的异常状况,就应判断为无故障。显然,这样做对某一实际的待检状态根本达不到诊断的目的,这是由于只利用先验概率提供的分类信息太少了。为此,我们还要对系统状态进行状态检测,分析所观测到的信息。

分类器模型2:CART: 分类与回归树

如果一个人必须去选择在很大范围的情形下性能都好的、同时不需要应用开发者付出很多的努力并且易于被终端用户理解的分类技术的话,那么Brieman, Friedman, Olshen和Stone(1984)提出的分类树方法是一个强有力的竞争者。我们将首先讨论这个分类的过程,然后在后续的节中我们将展示这个过程是如何被用来预测连续的因变量。Brieman等人用来实现这些过程的程序被称为分类和回归树(CART, Classification and Regression Trees)方法。

分类树

在分类树下面有两个关键的思想。第一个是关于递归地划分自变量空间的想法;第二个想法是用验证数据进行剪枝。

递归划分

让我们用变量y表示因变量(分类变量),用x1, x2, x3,...,xp表示自变量。通过递归的方式把关于变量x的p维空间划分为不重叠的矩形。这个划分是以递归方式完成的。首先,一个自变量被选择,比如 xi 和xi的一个值si,比方说选择si把p维空间为两部分:一部分是p维的超矩形,其中包含的点都满足

xi<=si,另一个p维超矩形包含所有的点满足xi>si。接着,这两部分中的一个部分通过选择一个变量和该变量的划分值以相似的方式被划分。这导致了三个矩形区域(从这里往后我们把超矩形都说成矩形)。随着这个过程的持续,我们得到的矩形越来越小。这个想法是把整个x空间划分为矩形,其中的每个小矩形都尽可能是同构的或“纯”的。“纯” 的意思是(矩形)所包含的点都属于同一类。我们认为包含的点都只属于一个类(当然,这不总是可能的,因为经常存在一些属于不同类的点,但这些点的自变量有完全相同的值)。

分类器模型3:kNN

邻近算法

k-Nearest Neighbor algorithm

左图中,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。

K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN 方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。

KNN算法不仅可以用于分类,还可以用于回归。通过找出一个样本的k个最近邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。更有用的方法是将不同距离的邻居对该样本产生的影响给予不同的权值(weight),如权值与距离成正比。

该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。因此可以采用权值的方法(和该样本距离小的邻居权值大)来改进。该方法的另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。目前常用的解决方法是

事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。

分类器模型4:AdaBoost

Adaboost 是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器 (强分类器)。其算法本身是通过改变数据分布来实现的,它根据每次训练集之中每个样本的分类是否正确,以及上次的总体分类的准确率,来确定每个样本的权值。将修改过权值的新数据集送给下层分类器进行训练,最后将每次训练得到的分类器最后融合起来,作为最后的决策分类器。使用adaboost分类器可以排除一些不必要的训练数据特徵,并将关键放在关键的训练数据上面。

目前,对adaboost算法的研究以及应用大多集中于分类问题,同时近年也出现了一些在回归问题上的应用。就其应用adaboost系列主要解决了: 两类问题、多类单标签问题、多类多标签问题、大类单标签问题,回归问题。它用全部的训练样本进行学习。

该算法其实是一个简单的弱分类算法提升过程,这个过程通过不断的训练,可以提高对数据的分类能力。整个过程如下所示:

1. 先通过对N个训练样本的学习得到第一个弱分类器;

2. 将分错的样本和其他的新数据一起构成一个新的N个的训练样本,通过对这个样本的学习得到第二个弱分类器;

3. 将第一次和第二次都分错了的样本加上其他的新样本构成另一个新的N个的训练样本,通过对这个样本的学习得到第三个弱分类器;

4. 最终经过提升的强分类器。即某个数据被分为哪一类要通过……的多数表决。

Adaboost(Adaptive Boosting)算法

对于boosting算法,存在两个问题:

1. 如何调整训练集,使得在训练集上训练的弱分类器得以进行;

2. 如何将训练得到的各个弱分类器联合起来形成强分类器。

针对以上两个问题,adaboost算法进行了调整:

1. 使用加权后选取的训练数据代替随机选取的训练样本,这样将训练的焦点集中在比较难分的训练数据样本上;

2. 将弱分类器联合起来,使用加权的投票机制代替平均投票机制。让分类效果好的弱分类器具有较大的权重,而分类效果差的分类器具有较小的权重。

Adaboost算法是Freund和Schapire根据在线分配算法提出的,他们详细分析了Adaboost算法错误率的上界,以及为了使强分类器达到错误率,算法所需要的最多迭代次数等相关问题。与Boosting算法不同的是,adaboost算法不需要预先知道弱学习算法学习正确率的下限即弱分类器的误差,并且最后得到的强分类器的分类精度依赖于所有弱分类器的分类精度,这样可以深入挖掘弱分类器算法的能力。

Adaboost算法中不同的训练集是通过调整每个样本对应的权重来实现的。开始时,每个样本对应的权重是相同的,即1/n 其中 n 为样本个数,在此样本分布下训练出一弱分类器。对于分类错误的样本,加大其对应的权重;而对于分类正确的样本,降低其权重,这样分错的样本就被突出出来,从而得到一个新的样本分布。在新的样本分布下,再次对弱分类器进行训练,得到弱分类器。依次类推,经过 T 次循环,得到 T 个弱分类器,把这 T 个弱分类器按一定的权重叠加(boost)起来,得到最终想要的强分类器。

Adaboost算法的具体步骤如下:

1. 给定训练样本集S,其中X和Y分别对应于正例样本和负例样本; T为训练的最大循环次数;

2. 初始化样本权重为1/n ,即为训练样本的初始概率分布;

3. 第一次迭代:

(1) 训练样本的概率分布相当下,训练弱分类器:

(2) 计算弱分类器的错误率:

(3) 选取合适阈值,使得错误率最小

(4) 更新样本权重:

(5) 最终得到的强分类器:

Adaboost算法是经过调整的Boosting算法,其能够对弱学习得到的弱分类器的错误进行适应性调整。上述算法中迭代了次的主循环,每一次循环根据当前的权重分布对样本x定一个分布P,然后对这个分布下的样本使用若学习算法得到一个错误率为的弱分类器,对于这个算法定义的弱学习算法,对所有的,都有,而这个错误率的上限并不需要事先知道,实际上。每一次迭代,都要对权重进行更新。更新的规则是:减小弱分类器分类效果较好的数据的概率,增大弱分类器分类效果较差的数据的概率。最终的分类器是个弱分类器的加权平均。

在前人的基础上,把OpenCV和MFC结合将视频显示出来了!哈皮ing

刚刚看了 OpenCV帮助文档里面的一个“读视频文件和运动问题检测”的例程,便简单用VC6.0的MFC对话

框程序做了下测试,感觉效果不错,故贴上来供和我一样的初学者做参考,做法如下(假设已经做好

OpenCV头文件及lib文件等设置):

1.用VC6.0的MFC建立一个对话框程序,设工程名为 ReadVideo;

2.在对话框上添加两个按钮:新建工程之后,在Resources下,找到Dialog,然后右击,选择“Insert

Dial og”,出现一个默认的OK按钮,和一个默认的CANCEL按钮。选择OK按钮,右击选择“Property”,将

ID改为IDC_FILE_OPEN,Caption改为打开视频,CANCEL按钮同样操作,ID为IDC_VIDEO_PRO,Caption 为处

理视频。然后,右击OK按钮键,选择“Class Wizard”,在左边找到IDC_FILE_OPEN,选择右边的BN—Clicked,然后单击Add Function,默认名字为OnFileOpen。同理对CANCEL按钮。对应的响应函数分别为

如下:(其中 strAviFilePath为在ReadVideoDlg.h声明的CReadVideoDlg类的Public成员变量即CString

strAviFilePath; // 存放所打开AVI视频文件的路径)

/***************************************************

* 打开AVI文件并获取文件路径

****************************************************/ void CReadVideoDlg::OnFileOpen()

{

// TODO: Add your command handler code here

// 文件打开对话框

CFileDialog dlg(true,"*.avi",NULL,NULL,"*.avi|*.avi||"); if (dlg.DoModal()==IDOK)

{

strAviFilePath = dlg.GetPathName();

}else

{

return;

}

}

/***************************************************

* 根据所选择的AVI文件进行视频处理

* 滑动平均背景建模,背景差进行运动检测

****************************************************/ void CReadVideoDlg::OnVideoPro()

{

// TODO: Add your command handler code here

//声明IplImage指针

IplImage* pFrame = NULL;

IplImage* pFrImg = NULL;

IplImage* pBkImg = NULL;

CvMat* pFrameMat = NULL;

CvMat* pFrMat = NULL;

CvMat* pBkMat = NULL;

CvCapture* pCapture = NULL;

int nFrmNum = 0;

//打开AVI视频文件

if(strAviFilePath=="") //判断文件路径是否为空

{

MessageBox("请先选择AVI视频文件!");

return;

}else

{

if(!(pCapture = cvCaptureFromFile(strAviFilePath)))

{

MessageBox("打开AVI视频文件失败!");

return;

}

}

//创建窗口

cvNamedWindow("Video", 1);

cvNamedWindow("Background",1);

cvNamedWindow("Foreground",1);

//使窗口有序排列,窗口宽330

cvMoveWindow("Video", 30, 0);

cvMoveWindow("Background", 360, 0);

cvMoveWindow("Foreground", 690, 0);

//逐帧读取视频

while(pFrame = cvQueryFrame( pCapture ))

{

nFrmNum++;

//如果是第一帧,需要申请内存,并初始化

if(nFrmNum == 1)

{

pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); // 存放背景图

像(灰度)

pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); // 存放中间图像(灰度)

pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);

pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);

pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);

//转化成单通道图像再处理(灰度)

cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);

cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);

cvConvert(pFrImg, pFrameMat);

cvConvert(pFrImg, pFrMat);

cvConvert(pFrImg, pBkMat);

}

else

{

cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY); //转化成单通道图像再处理(灰度) cvConvert(pFrImg, pFrameMat);

//高斯滤波先,以平滑图像

//cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0);

//当前帧跟背景图相减(求背景差并取绝对值)

cvAbsDiff(pFrameMat, pBkMat, pFrMat);

//二值化前景图(这里采用特定阈值进行二值化)

cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY);

//进行形态学滤波,去掉噪音

cvErode(pFrImg, pFrImg, 0, 1);

cvDilate(pFrImg, pFrImg, 0, 1);

//滑动平均更新背景(求平均)

cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);

//将背景转化为图像格式,用以显示

cvConvert(pBkMat, pBkImg);

// 保持原图像的旋转方向

pBkImg->origin = pFrImg->origin = pFrame->origin;

//显示图像

cvShowImage("Video", pFrame);

cvShowImage("Background", pBkImg);

cvShowImage("Foreground", pFrImg);

//如果有按键事件,则跳出循环

//此等待也为cvShowImage函数提供时间完成显示

//等待时间可以根据CPU速度调整

if( cvWaitKey(200) >= 0 )

break;

}

}

//销毁窗口

cvDestroyWindow("Video");

cvDestroyWindow("Background");

cvDestroyWindow("Foreground");

//释放图像和矩阵

cvReleaseImage(&pFrImg);

cvReleaseImage(&pBkImg);

cvReleaseMat(&pFrameMat);

cvReleaseMat(&pFrMat);

cvReleaseMat(&pBkMat);

cvReleaseCapture(&pCapture);

}

然后再视图类的源文件中添加包含对话框头文件的命令:#include “CReadVideo.h”

然后在Classes中找到视图类,右击,选择“Add Windows Handle。。。”,从左边选择“WM_LBUTTON”,

单击“Add and Edit”,回到OnLButtonDown函数处,添加代码:

CReadVideoDlg dlg;

dlg.DoModal();

3.编译后运行程序,并找一个合适的背景静止的AVI视频文件即可看到效果;

4.*.exe文件需和相应的dll文件放在一起。

5.运行的效果图如图1所示,从左至右依次为视频原图、背景图、运动区域二值图。

该例子的二值化阈值可以考虑用自适应更新的方式,背景模型也可以用其它更有效的方法。感觉OpenCV函数的功能还是很强大的,实现同样的效果利用OpenCV可以更节省时间,恩,性价比较高,赞!

视频显示的一个程序,转载

#include

#include

#include

int main( int argc, char** argv )

{

//声明IplImage指针

IplImage* pFrame = NULL;

IplImage* pFrImg = NULL;

IplImage* pBkImg = NULL;

CvMat* pFrameMat = NULL;

CvMat* pFrMat = NULL;

CvMat* pBkMat = NULL;

CvCapture* pCapture = NULL;

相关文档