1、1小规模图像样本分类实验学院:计算机科学与技术2一 课题分析 31.1 原始数据 .31.2 最终结果 .31.3 处理过程 .4二 框架设计 42.1 混合编程环境的搭建 .52.1.1 基本架构 52.1.2 matlab 环境的设置 52.1.3 matlab 中动态链接库的编译 72.1.4 matlab 生成文件 72.1.5 vc6 中环境的搭建 .82.2 底层处理 .92.2.1 模块分析 92.3 上层接口 .102.3.1 逻辑控制模块组成 102.3.2 人机接口的设计 11三 具体实现 113.1 底层处理 .113.1.1 读取测试集 113.1.2 读取测试集 12
2、3.1.3 LDA 求解转换矩阵 Wt 123.1.4 使用 Wt 压缩矩阵 .133.1.5 使用最近邻算法对测试集进行分类 133.2 上层接口之逻辑控制模块 .143.2.1 读取训练集 143.2.2 读取测试集 153.2.3 LDA 求解转换矩阵 Wt 153.2.4 使用 Wt 压缩矩阵 .163.2.5 使用最近邻算法对测试集进行分类 163.3 上层接口之人机接口设计 .173.3.1 菜单栏的设计 183.3.2 CEdit 的使用 .183.3.3 管道重定向 193.3.4 流程控制 20四 程序展示 224.1 程序展示 .224.1.1 读取训练集和测试集 224.
3、1.2 LDA 求 Wt 234.1.3 Wt 压缩训练集和测试集 244.1.4 最近邻进行分类 25五 总结 263一 课题分析1.1 原始数据原始数据由两大部分组成:训练集和测试集。训练集由 200 个训练类组成,即代表有 200 个相应的类。每个类中存放着表示此类的特征向量 144 个,每个特征向量由 440 维组成,基本单位是 float 型,32 位。测试集由 200 测试类组成,即代表有 200 待测试的相应的类。每个类中存放着待测试的特征向量 18 个,每个测试向量由 440 维组成,基本类型为 float 行,32 位。对训练集进一步说明。训练集中每个类(即对应的 f*.da
4、t 文件)中的每个样本数据(共 144 个)表示了这个类的特征,但是单一的一个样本不能全面准确的反应这个类的准确信息,有多组有区别样本才能更好的体现这个类的特征。例如:此时试验中,每个训练类有 144 个训练样本,其中每个样本都代表了这个类的特征,而每个样本又有细微的区别,只有这样才能比较全面的反应这个类的特征。对测试集进一步说明。测试集由 18 个样本组成,对每个样本进行测试,并且得出此个样本所属的类别。说明:某一测试类中每个样本都是用来测试的,不是用来训练的。图 1-1 训练类按行数据模式存储模式图 1-2 测试类按行数据模式存储模式41.2 最终结果最终结果为对每个测试类测试之后,返回对
5、这个类的测试时间,分类的正确率。对总共 200 个测试类测试后返回总的测试时间和总的测试正确率。详见程序展示。1.3 处理过程对原始数据的处理可以按照以下的步骤进行:读取训练集数据读取测试集数据L D A 算法求 W t使用 W t 进行特征压缩使用最近邻进行分类原始数据训练集矩阵测试集矩阵W t 矩阵压缩训练集矩阵压缩测试集矩阵测试时间矩阵测试正确率矩阵图 1-3 数据处理流程这是一个总体的流程,具体步骤后面阐述。二 框架设计这个实验中,很多地方需要复杂的矩阵计算,比如求特征值,求特征向量,求矩阵的逆矩阵等,所以可以考虑使用 matlab 来进行计算。但是,考虑的 matlab 的做上层界面
6、的即控制流程的复杂型,界面以及逻辑层,即上层实现还是使用 vc 来实现。这样最终的编程模5式就是 matlab+vc6 的混合编程。R e a d T r a i n .mR e a d T e s t . m .mL D A . mF e a t u r e C o mp r e s s i o n . mT h e 1 n n . mR e a d T r a i n . ex eR e a d T e s t . m. e x eL D A . e x eF e a t u r e C o mp r e s s i o n . e x e T h e 1 n n . e x eV c _
7、 p r o j e c t( 人机交互 )D l lD l l D l l D l l D l lP i p e P i p e P i p e P i p e P i p e逻辑控制层底层人机交互层图 2-1 3 层结构2.1 混合编程环境的搭建2.1.1 基本架构基于 matlab+vc6 的混合编程使用这样的架构,matlab 对底层算法进行实现,然后使用mcc 将其编译成动态链接库 dll,最终 vc6 调用动态链接库里的相应的函数来使用 matlab 底层引擎。v c 6e x ed l lm c c编译编译调用图 2-2 matlab 与 vc 混合编程框架图2.1.2 matl
8、ab 环境的设置Matlab 的环境配置:配置 mbuild setup6图 2-3 mbuild 的配置Matlab 的环境配置:配置 mex setup图 2-4 mex 的配置Matlab 环境配置:配置路径, matlab 安装的时候已经加入了环境标量,如果未加入请手工加入 matlab 对应环境变量。7图 2-5 配置 matlab 路径2.1.3 matlab 中动态链接库的编译Matlab 可以通过 mcc 将*.m 文件编译成 dll 文件,及动态链接库。这个在 vc 的环境下就可以调用动态链接库里的函数了。Mcc 对应的编译动态链接库的命令为:mcc -W lib:yourl
9、ib -T link:lib test1.m test2.m -v。其中-W lib 命令的意思是将 test1.m,test2.m中的函数封装到动态链接库形式的函数;-T link:lib 的意思是将各个函数封装成一个整体的动态链接库。需要说明的是如 test1.m 形式的文件,其中只能包含一个对外部可见的函数,而且其名称最好与文件名相同。例如 :function ret = test1(args).2.1.4 matlab 生成文件对相应的*.m 文件进行编译:图 2-6 编译 m 文件经过 mcc 的编译后,会生成许多文件。例如:8图 2-7 mcc 编译后生成的文件其中选中的是我们编译
10、时候要用到的,其中*.ctf 比较特殊,他的作用通过它调用matlab 引擎底层。所以,一般情况下编译的时候要用到这 4 个文件,而运行时只需要*.dll 和 *.ctf 就可以 。特别说明,如果需要重新编译 m 文件的话,建议删除以前编译文件以后再重新编译,可以避免符号表不一致的问题。2.1.5 vc6 中环境的搭建Vc6 中除了要有 mcc 生成的*.h , *.lib , *.dll , *.ctf 文件以外,还需要进行一些配置才可以进行相应的编译工作。1首先,需要将 matlab 和 vc6 混合编程所需要的库导入:图 2-8 导入 mclmcrrt.lib 库2然后,初始化和停止 m
11、atlab api 调用,都有特定的函数,用法分别为:初始化调用 api:if(!mclInitializeApplication(NULL,0)printf(“Could not init appn“);return -1;停止调用 apimclTerminateApplication();需要说明的是,只有初始化了 api 调用函数,才能调用 matlab 引擎中相应的函数。3接着需要包含所调用库的头文件和导入 lib#include “readtrain.h“#pragma comment(lib,“readtrain.lib“)94初始化动态链接库,停止动态链接库初始化:if (!re
12、adtrainInitialize()printf(“Could not init libn“);return -1;停止readtrainTerminate();这两个函数在使用 mcc 生成的*.h 文件中都有。5调用相应的函数完成相应的功能根据具体的情况对待。2.2 底层处理2.2.1 模块分析根据图 1-3 可以知道,需要 matlab 处理的模块有 5 个,分别为:读取原始训练集,读取原始测试集,LDA 算法生成 Wt,使用 Wt 算法压缩特征值,使用最近邻算法分类。底层L D AF e a t u r e C om p r e s s i o n1 _ n nR e a d T r
13、 a i nR e a d T e s t图 2-9 底层模块图10图 2-10 对应的目录结构2.3 上层接口上层接口使用 vc6 实现,包括逻辑控制层和人机交互层。其中逻辑控制层和人家交互层使用管道通信,这是由于逻辑控制层使用的控制台模式,而在 mfc 框架中要使用一个独立的控制台程序就需要建立管道通信来实现两个进程之间的通信。其中,管道是单工的,数据流向:从逻辑控制层到人机接口。2.3.1 逻辑控制模块组成从图 2-1 可以看出,逻辑控制的模块组成与底层建立一一映射,结构类似。设立逻辑控制模块的意义在于,底层的 matlab 模块编译成动态链接库之后,其中可能不只含有一个函数,多个函数的
14、调用顺序以及调用传入参数和传出参数的处理都需要进行相应的处理;并且对于上层的人机接口,应该给予适当的说明信息。综上逻辑控制模块是一个统筹底层模块,统一信息给人机交互模块的中介层。图 2-11 逻辑控制模块目录结构每个底层模块中对应的 vc_test 目录是对逻辑控制模块的实现,为控制台程序。112.3.2 人机接口的设计人机接口综合所有功能到一起,给用户提供一个使用所有功能的接口,使用 mfc 开发,详见具体实现。三 具体实现3.1 底层处理使用 matlab 编写相应的 m 文件,并编译成动态链接库共上层调用。对具体讨论之前先约定一些名称。训练集矩阵 M_TRAIN 28800,440测试集
15、矩阵 M_TEST 3600,440类平均值矩阵 ui 200,440总平均值矩阵 u 1,440类内散布矩阵 SW 440,440类间散布矩阵 SB 440,440Wt 转换矩阵 Wt 199,440压缩后的训练集矩阵 M_TRAIN 199,28800压缩后的测试集矩阵 M_TEST 199,3600测试集匹配矩阵 I 200,18 i,j测试类 i 的第 j 个样本匹配到Ii,j注意,训练集矩阵,测试集矩阵在压缩前后其名称并未改变,但请注意他的位数,以及是行向量还是列向量。3.1.1 读取测试集读取测试集对应的是工程 matlab7.0 工程mlib_2_1readtrain readt
16、rain.m 文件。所完成的功能是读取一个训练类存入训练类矩阵文件中。12传出 : 返回值传入 : 训练集文件名训练集矩阵文件名设置基本参数C = 2 0 0 k = 4 4 0n i = 1 4 4 n j = 1 8 ;打开训练集文件 ?读取文件正确 ?打开训练集矩阵文件名 ?写训练集矩阵到训练集矩阵文件尾部按行向量存放到训练集矩阵设置返回值为 1 = 正确 设置返回值为负数 = 错误是是是是否图 3-1 读取训练集流程图底层对读取训练集就是每次读出一个训练集中的一个类,然后存入训练集矩阵之中,最终要达到的效果就是,所有训练类的特征值都存放在一个特征矩阵文件中,方便以后操作。对于本实验就是
17、最终存入的训练集矩阵 28800*440,单位 float。3.1.2 读取测试集 可以类同读取测试集分析。3.1.3 LDA 求解转换矩阵 Wt转换矩阵的 Wt 的求解依赖 LDA 算法。LDA 算法可以分为 6 个部分来实现,具体的实现位于工程matlab7.0 工程mlib_2_1lda*.m 文件。Lda 算法的核心思想就是按照类间距离/ 类内距离 = 最大值的规则来求解转换矩阵最终达到压缩特征维数的效果的算法。ldagettrain1.m 文件,转换矩阵的求解的原始数据是训练集中所有样本,对于本实验来说就是 200*144 = 28800 个样本,通过这 200 个类,共 28800
18、 个样本,根据类间距离/类内距离最大化的原则求解相应的转换矩阵 Wt。所以第一步应该是获取 28800*440(float)的训练集矩阵。而 ldagettrain1.m 文成的就是这个工作。ldagetui2.m 文件,获取每个类中样本的平均值。 ui 是一个 200*440(float)的矩阵13ldageu3.m 文件,获取所有样本的平均值。u 是一个 1*440(float)的矩阵。ldagetsw4.m 文件,获取类内散布矩阵,其实就是求解每个训练类的协方差之和,表明了类内的离散程度,SW 是 440*440(float)矩阵。ldagetsb5.m 文件,获取类间散布矩阵,表明了类
19、间的离散程度,SB 是一个440*440(float)的矩阵。ldagetwt6.m 文件,获取最终的转换矩阵,其核心算法是求 inv(SW)*SB 的特征向量,按从大到小的顺序排列非零特征值并且取出相对的特征向量组成转换矩阵 W,并将其特征向量按行向量排列得到 Wt。 在本实验中 Wt 是 199*440 的矩阵。1 获取训练集矩阵 M _ T R A I N2 计算类内平均值 u i3 计算总的平均值 u4 计算类内矩阵S W5 计算类间矩阵S B计算T = i n v ( S W ) * S B计算 T 的特征值和特征向量按特征值从大到小的顺序排列特征值和特征向量取特征值不为 0的特征值
20、对应的特征向量做成矩阵 W对 w 进行转置得到 W t6图 3-2 lda 算法求转换矩阵 Wt3.1.4 使用 Wt 压缩矩阵使用 LDA 算法得到 Wt 矩阵之后就可以使用 Wt 矩阵对原始的训练集矩阵,测试矩阵进行压缩。压缩的方法也很简单,使用 Wt*M_TRAIN和 Wt*M_TEST就可以了。具体的实现位于工程matlab7.0 工程mlib_2_1featurecompression featurecompression.m 文件。3.1.5 使用最近邻算法对测试集进行分类最近邻算法的思想就是对进行测试的每个样本 x,求样本 x 到某个类Yi,i=1,2,3200 的欧式距离,然后
21、将距离最小类 Yk 判定为 x 的分类目标,即 x 属于类 Yk。基于此算法,可以设计最近邻算法:读取训练集矩阵,读取测试集矩阵;对测试集矩阵的每一个类 X 的每一个样本 xi 进行测试,看这个样本 xi 是否属于正确的类 X,进而得到每个测试类的测试时间和每个测试类的识别正确率。工程matlab7.0 工程mlib_2_11nn the1nn1.m,the1nn2.m.The1nn1.m 实现的是读出压缩的训练集矩阵,测试集矩阵到内存。The1nn2.m 实现的是对类 r(共 200 个测试类)进行测试,并且返回测时间,和识别正确率。算法如下:%开始计时tic;%外层循环负责定位类 r 在压
22、缩测试集矩阵中的位置 k,表示为类 r 的第 1,2,3nj 个样本for k=(r-1)*nj+1:r*nj%中层循环负责计算第 k 个样本与训练集 i 的欧式距离,并记录在 d(k,i)中for i=1:c14%内层循环负责计算训练集 i 中每个样本(共 144 个) 与第 k 个测试样的欧式距离并且叠加来求与类 i 的欧式距离for j=1:nid(k,i) = d(k,i) +(M_TEST(:,k)-M_TRAIN(:,(i-1)*ni+j)*(M_TEST(:,k)-M_TRAIN(:,(i-1)*ni+j);endendend%计时结束t(r) = toc;%返回值计时结构 re
23、ttimerettime = t(r);%测试集匹配矩阵 I 200,18 i,j测试类 i 的第 j 个样本匹配到 Ii,jC,I = min(d);%转换成 unsigned int 格式,返回值I = uint32(I);3.2 上层接口之逻辑控制模块控制逻辑为与底层模块相对应,位于各个底层模块的文件夹中,例如:工程matlab7.0工程mlib_2_1readtrainvc_test 就是一个基于控制台的逻辑控制模块。他所要完成的任务就是就是整合 m 文件编译成的函数形成统一的接口供上层人机接口调用,达到统一化,方便化人机接口调用的目的。m 库函数 1m 库函数 2m 库函数 3某个逻
24、辑控制模块统一信息到人机接口图 3-3 底层模块与逻辑控制模块的关系3.2.1 读取训练集读取训练集文件控制模块中,底层 m 文件实现的是对某一个训练集文件读取后存入训15练集矩阵,即某个训练类的读取后存入矩阵。所以,此处逻辑控制模块要做的就是通过一个循环将 200 个训练类都读入,然后统一存入一个训练集矩阵。开始结束F o r I = 1 2 0 0i = 1 ?删除已有训练集矩阵文件读入训练集集中的类 I , 写入训练集矩阵文件尾部是否I 是 1 2 0 0打印统一信息到s t d o u t图 3-4 逻辑控制模块训练集读取流程图最后形成 M_TRAIN 未压缩的矩阵形式 28800*4
25、40。3.2.2 读取测试集 读取测试集可以仿照读取训练集来分析。3.2.3 LDA 求解转换矩阵 WtLDA 求解转换矩阵的底层动态链接库中共包含 6 个函数的实现,在逻辑控制层就需要对其进行整合,并向人机接口层打印各个函数返回的统一信息。16开始结束获取训练集矩阵并打印获取信息到 s t d o u t计算每个训练类样本的平均值 u i矩阵并打印信息到s t d o u t计算所有样本的平均值 u 矩阵并打印信息到s t d o u t计算 s w 矩阵并打印信息到s t d o u t计算 s b 矩阵并打印信息到s t d o u t计算 w t 矩阵并打印信息到s t d o u t
26、将 W t 矩阵信息写入W t 矩阵文件包含在一个函数内图 3-5 逻辑层 lda 的逻辑控制流程LDA 最终计算得出转换矩阵 Wt,这是一个 199*440 格式的矩阵。3.2.4 使用 Wt 压缩矩阵当得到了转换矩阵 Wt 以后,对原始的训练集矩阵,测试集矩阵的压缩就比较简单了。直接用 Wt 乘以未压缩的训练集矩阵转置 M_TRAIN,测试集矩阵转置即可。最后得到的是压缩训练集矩阵的 M_TRAIN(格式 199*28800)和压缩的测试集矩阵 M_TEST(格式199*3600),为列向量的存储方法。对应到相应的 m 库函数函数只有一个,直接调用即可。3.2.5 使用最近邻算法对测试集进
27、行分类最近邻算法封装的底层库函数有两个,一个是负责压缩读取训练集矩阵和压缩的测试集矩阵的;另外一个是负责最某一个测试类进行分类的函数。所以逻辑控制层对其支持就是,读取压缩的训练集矩阵和压缩的测试集矩阵循环对 200 个测试类进行分类并且返回没个测试类的分类精度和分类速度。17开始结束读取压缩的训练集矩阵和压缩的测试矩阵F o r i = 1 2 0 0使用最近邻算法分类 ,打印分类精度和速度到 s t d o u t存储分类精度 , 分类速度信息到文件图 3-6 逻辑层最近邻算法流程3.3 上层接口之人机接口设计人机接口的设计使用 mfc 开发,基于单文档的一个视窗系统。人机接口和逻辑控制采用
28、单工的管道实现:mfc 建立逻辑成模块的进程mfc 程序建立管道信息流向是从逻辑控制层的控制台程序的 stdout 中向 CEdit 控件。这样就完成了 mfc 打开逻辑控制层,并且从逻辑控制层接受打印消息的功能。M f cC e d i t读取训练集 读取测试集 L D A 算法求 W t 压缩特征值 最近邻分类P i p eP i p eP i p eP i p eP i p e图 3-7 逻辑层和人机接口层关系需要说明的是 mfc 打开逻辑控制层模块,也就是 mfc 创建一个控制台程序的时候,如果控制台程序有路径相关的参数,要特别注意路径中是否带有空格,如:C:Document and
29、settings 这样的路径是不能正确的作为参数的。控制台程序会理解为 C:Document 是路径而忽略了空格后面的部分。解决的办法是使用 dos 路径,即短路径的方法。短路径由 8 个字符组成,不包含空格,如 C:DOCUME1 的格式,对其进行解释,前 6 个字符为长路径的前 6 个字符,代表省略,1 代表此个路径排序第一个,例如如果又有 C:Document other 路径是,可以将18C:Document other 缩写成 C:DOCUME2。具体实现长路径到短路径,可以调用 apiDWORD GetShortPathName(LPCTSTR lpszLongPath, / nu
30、ll-terminated path stringLPTSTR lpszShortPath, / short form bufferDWORD cchBuffer / size of short form buffer);用法如下:char cFolderMAX_PATH = “C:Document and settings“; char cFolderShortMAX_PATH = “;GetShortPathName(cFolder,cFolderShort,sizeof(cFolderShort);这样 cFolderShort 存储的就是 C:DOCUME1 了。人机接口对应的工程为:
31、工程vc6 工程此个 mfc 程序包含了一个菜单栏的设计,包括子菜单;一个 CEdit 的使用。3.3.1 菜单栏的设计菜单栏设计如下:图 3-8 菜单栏的设计菜单栏的设计包含 5 个部分:1)打开所有的特征值这一项所完成的功能是打开原始的训练集和测试集,并将其保存为训练集矩阵和测试集矩阵。2)LDA 转换矩阵 wt这一项完成的功能是使用 LDA 算法就是转换矩阵 Wt 并保存到相应的文件中。3)LDA 压缩特征值这一项完成的功能是使用 Wt 对原始的 440 特征值进行压缩,得到压缩后 199 维的特征值。4)分类测试 1-NN这一项完成的是使用最近邻算法进行分类,并获取分类精度与分类速度。
32、5)重新开始在程序执行的任何时刻,如果需要重新开始的话,你可以使用这个菜单。193.3.2 CEdit 的使用CEdit 是用来从管道接受并且显示数据的。下面将一步步分析如何实现 CEdit 的管道通信。首先,在视图类中声明私有变量private:CEdit m_Edit;CFont m_Font;然后,在视图类中的 OnCreate 消息中加入创建函数并且设置好字体的种类以及大小。接着,在视图类的 OnSize 中实现其大小的控制这样一个 CEdit 控件就加入了单文档程序的驶入类中了。3.3.3 管道重定向本程序使用管道重定向类,是来自开源代码中的一部分。其授权描述如下:20首先,将对应的
33、 Redirect.h 和 Redirect.cpp 包含到工程图 3-9 Redirect 对应文件加入工程然后,在视图类中加入其私有变量CRedirect *m_pRedirect;然后,构造函数中需要酒气初始化m_pRedirect = NULL;接着,可以进行使用例如:CString Command = “.AddInLda.exe ReadTrain_28800_440.float32“;/Command 包含了要创建的控制台程序以及传入参数,用空格隔开,使用短路径m_pRedirect = new CRedirect(LPTSTR)(LPCTSTR)Command, UpdateC
34、ontrols();m_pRedirect-Run();delete m_pRedirect;21m_pRedirect = 0;UpdateControls();3.3.4 流程控制对于程序的执行流程需要一定的控制。首先,在视图类中声明状态标识int state;然后,在视图类构造函数中初始化state = -1;接着,编写控制函数详见代码视图类中 UpdateControls()函数开始结束获取正确 ?获取菜单栏正在重定向 ?屏蔽所有菜单使能下一步菜单是是否否图 3-10 UpdateControls 函数流程具体实现效果如下:22第一步 第二步 第三步 第四步图 3-11 流程四 程序展
35、示4.1 程序展示4.1.1 读取训练集和测试集图 4-1 打开所有特征值23图 4-2 打开训练集和测试集目录图 4-3 读取过程图 4-4 读取结束244.1.2 LDA 求 Wt图 4-5 结束 Wt图 4-6 求解 Wt 信息图 4-7 求解完成254.1.3 Wt 压缩训练集和测试集图 4-8 压缩特征值图 4-9 压缩信息图 4-10 压缩结束264.1.4 最近邻进行分类 统一 4-11 最近邻分类图 4-12 分类信息27图 4-13 分类结果图 4-14 分类结束五 总结此次小规模图像样本分类实验,使用特征压缩算法是 LDA,分类算法使用的是最近邻。最终的分类速度是 471.738000 s;分类精度是 97.666667%。测试环境:CPU :Intel(R) Core(TM)2 Duo CPU P8600 2.4GHz内存:2.94GB