matlab+java在web应用程序上实现人脸匹配功能

       大家好,这是小编第一次写博客(心里有点紧张……),以下哪里写的不对的地方请大家多多包涵。以前写了很多程序,现在想重新写一次以前的程序突然发现有不会写,想了想以后还是写几篇博客将写过的程序的过程记录下来,以便日后使用。好吧,废话不多说,我们开始吧。

       今年暑假实训之前还以为实训内容是做关于java web的demo,等到实训开始的时候才知道要做的是关于科研类型的demo,最后选了个人脸匹配。刚开始的时候还是跟以往一样,先去百度,有没有现成的代码可以看看,结果是没有找到。刚开始的时候是打算使用OpenCV实现人脸匹配算法,但是无奈小编C++的基础知识已经忘得差不多了,而且记得以前OpenCV的环境配置起来还挺麻烦的,现在又忘了当时是怎么配置的了,小编英语基础又不好,不会看官方文档,然后又去查了JavaCV关于人脸匹配这一块的情况,还是没有求到符合要求的代码,网上大多数关于人脸匹配的代码都是人脸检测的代码,并不是小编想要的人脸匹配的代码,最后我找到了一个使用MATLAB实现人脸匹配算法的博客,这个博客的地址是:Matlab PCA+SVM人脸识别(一),现在我们使用java实现它吧。

请原谅高数不好的小编,并没有看懂其中PCA+SVM实现的人脸识别算法,所以不再这里重述,如果对使用PCA+SUM人脸识别算法有兴趣的朋友可以去参考一下该博客的内容。好吧,我们开始第一步,先去下载该博客博主分享的代码(为了尊重博主,这里不提供下载地址),我们下载下来解压后里面有这些文件:


       噢,忘了,我们应该先安装MATLAB这个软件的,小编是用的是MATLAB2012,注意,使用jdk8的朋友得装回jdk7因为MATLAB2012好像不能识别出jdk8,不能识别jdk后面就不能将m函数文件打包成jar包供java程序使用了。MATLAB安装好了之后是这个样子的:



       关于MATLAB的使用说明这里小编就不多说了,如果不会使用MATLAB的朋友可以去我要自学网查看相关视频教程,小编本来也是不会使用MATLAB的,在自学网上面看了一点点教程就会了,当然这里我们只使用一些非常简单的操作(我可不是在打广告哈)。打开MATLAB之后,左边打开的应该是MATLAB的安装目录,我们新建一个目录,把我们下载的文件全部拷贝上去,图中的hcimage文件夹是我后来加进去的,这个是为了后面选择图片的时候方便一点。好吧,人脸识别总不能没有人脸吧,因为博主使用的是ORL人脸库,所以小编又去网上下载了人脸库,下载下来应该是这个样子的:


       一共有40个人脸图像,也就是到s40为止,其中每个人都有十张图片,BMP格式的,我们打开ReadFace.m文件,会发现有这么一段:这意味着我们得将我们下载的ORL人脸库放在E盘根目录下面,或者更改这里的路径,小编是将图片放在E盘根目录下面,以防出现其他问题。接下来我们去Readface.m和GUIopen.m和GUIrecg.m三个文件将下面的“.pgm”改成“.bmp”;



       好了,接下我们就可以启动了,我们在CommandWindow中敲入faceGUI然后回车,接着就应该出现人脸识别的界面了:



       出现界面了是不是有点小激动呢?好吧,现在我们来随便操作一下这个demo,操作完了之后我们就该干正事了,我们怎么才能使用Java实现人脸识别呢?MATLAB中的.m文件有些是脚本文件,有些是函数文件(小编自己编的,具体小编也不清楚该怎么称呼),但是两者都可以直接执行,但是我们将脚本文件打包成jar包调用的时候,会出现异常,只有函数类型的.m文件才能正常调用,所以,现在我们得将我们需要的操作封装到一个函数里面去。现在我们来看看那些使我们需要的操作,首先打开face.m,这个文件是加载训练集的脚本文件,加载训练集是什么意思?我们可以看到,右边有一些我们看不懂的东西:



       当我们在Command Window中使用变量的时候就会保存到内存中,直到我们手动删除变量或者退出MATLAB的时候才会释放内存,第一列就是变量的名称,我们可以在Command Window中敲入某个变量的名字,然后回车,这里小编敲的是multiSVMstruct,然后我们可以看见内容:



      这些内容就是变量multiSVMstruct的值,这里是一个矩阵。我们还可以将这些变量保存到一个文件中,就是face.m文件中的这句代码:


       这个文件就是我们外面的recognise.mat,这个就是博主训练40个训练集得到的结果,她将结果保存在一个.mat文件中,如果我们删除这个文件,那么人脸识别程序就不能正常运行了,我们在Command Window中敲入face后就会重新生成recognise.mat文件(不要忘了人脸库)。现在我们将其封装成一个函数,将不必要的部分去除,其中


等等是不必要的代码,其作用是向控制台或UI界面输出消息。

这个是原来的代码:

clc,clear
npersons=40;%选取40个人的脸
global imgrow;
global imgcol;
global edit2
imgrow=112;
imgcol=92;

set(edit2,'string','读取训练数据......')
drawnow
f_matrix=ReadFace(npersons,0);%读取训练数据
nfaces=size(f_matrix,1);%样本人脸的数量

set(edit2,'string','训练数据PCA特征提取......')
drawnow
mA=mean(f_matrix);
k=20;%降维至20维
[pcaface,V]=fastPCA(f_matrix,k,mA);%主成分分析法特征提取

set(edit2,'string','训练数据规范化......')
drawnow
lowvec=min(pcaface);
upvec=max(pcaface);
scaledface = scaling( pcaface,lowvec,upvec);

set(edit2,'string','SVM样本训练......')
drawnow
gamma=0.0078;
c=128;
multiSVMstruct=multiSVMtrain( scaledface,npersons,gamma,c);
save('recognize.mat','multiSVMstruct','npersons','k','mA','V','lowvec','upvec');

set(edit2,'string','读取测试数据......')
drawnow
[testface,realclass]=ReadFace(npersons,1);

set(edit2,'string','测试数据特征降维......')
drawnow
m=size(testface,1);
for i=1:m
    testface(i,:)=testface(i,:)-mA;
end
pcatestface=testface*V;

set(edit2,'string','测试数据规范化......')
drawnow
scaledtestface = scaling( pcatestface,lowvec,upvec);

set(edit2,'string','SVM样本分类......')
drawnow
class= multiSVM(scaledtestface,multiSVMstruct,npersons);
set(edit2,'string','测试完成!')
accuracy=sum(class==realclass)/length(class);
msgbox(['识别准确率:',num2str(accuracy*100),'%。'])


这个是小编更改后的代码:

function [ output_args ] = face( ~ )
%FACE Summary of this function goes here
%   Detailed explanation goes here
clc,clear
npersons=6;%选取6个人的脸
global imgrow;
global imgcol;
global edit2
imgrow=112;
imgcol=92;
f_matrix=ReadFace(npersons,0);%读取训练数据
nfaces=size(f_matrix,1);%样本人脸的数量
mA=mean(f_matrix);
k=20;%降维至20维
[pcaface,V]=fastPCA(f_matrix,k,mA);%主成分分析法特征提取

lowvec=min(pcaface);
upvec=max(pcaface);
scaledface = scaling( pcaface,lowvec,upvec);
gamma=0.0078;
c=128;
multiSVMstruct=multiSVMtrain( scaledface,npersons,gamma,c);
save('recognize.mat','multiSVMstruct','npersons','k','mA','V','lowvec','upvec');
[testface,realclass]=ReadFace(npersons,1);
m=size(testface,1);
for i=1:m
    testface(i,:)=testface(i,:)-mA;
end
pcatestface=testface*V;
scaledtestface = scaling( pcatestface,lowvec,upvec);
class= multiSVM(scaledtestface,multiSVMstruct,npersons);
accuracy=sum(class==realclass)/length(class);
%msgbox(['识别准确率:',num2str(accuracy*100),'%。'])
output_args = 0;
end


这个函数的作用是加载训练集,输出结果为测试训练集的准确率,参数无。

       接下来我们接着看scaling.m、ReadFace.m、multiSVMtrain.m、multiSVM.m、kfun_rbf.m、fastPCA.m文件,这些文件本身就是一个函数文件,我们就不去管它,具体作用注释得很明白了。然后是faceGUI.m、GUIopen.m、GUIrecg.m分别是人脸识别主界面,图片选择界面和人脸识别程序,这三个也都是脚本文件。我们需要将需要的操作抽取出来作为一个函数调用,也就是人脸识别的程序,另外两个界面程序我们可以使用java实现。我们把GUIrecg.m文件中的主要代码抽取出来,以识别的图片的路径作为参数,将识别的结果返回。这里我新建了一个Recg.m文件保存了起来。以下为Recg.m代码:

function [ number ] = Recg( filepath )
%RECG Summary of this function goes here
%   Detailed explanation goes here

img = imread(filepath);

  if isempty(img)
    msgbox('请先选择一张图片!')
    return
  end
  if ~exist('recognise.mat','file')
      face();
      disp('hello');
  end
load('recognize.mat');
testface=img(:)';
Z=double(testface)-mA;
pcatestface=Z*V;
scaledtestface=-1+(pcatestface-lowvec)./(upvec-lowvec)*2;
voting=zeros(1,npersons);
for i=1:npersons-1
    for j=i+1:npersons
        class=svmclassify(multiSVMstruct{i}{j},scaledtestface);
        voting(i)=voting(i)+(class==1);
        voting(j)=voting(j)+(class==0);
    end
end
[~,class]=max(voting);
number = num2str(class);
end

       好了,当我们想要识别人脸图片的时候,就先调用一下face文件加载训练集,然后就调用Recg函数传入图片的路径就可以得到结果了,也不是很难,注意,第一次加载训练集的时候会有点慢,大概要十多秒这样子。这里我还把face_one的主要代码也抽取出来写成另外一个函数,这个函数的功能是测试算法的准确率,每人有十张图片,头五张作为训练集,后五张作为测试集,最后将得出的准确率返回,这里我写在了一个accuracyRate.m文件中,但是在java程序中我并没有使用这个函数。现在我们将函数打包,在Command Window中敲入deploytool,选择Java package,点击OK。



       在右边的java package ->中选择add class,class 名称随便填,首字母最好大写,符合Java类名规范,以后调用函数的时候就是通过这个类名调用的,填写好类名之后就点击add file,选中我们刚才写好的函数,这里小编偷了个懒,选中了所有的文件,以防万一漏了了那个文件导致无法正常调用函数,然后我们点击最左边的这个build按钮。稍等一会,出现下面这个界面就表示成功了,注意,这里小编用的是MATLAB2012,如果装的是jdk8的话就不会有Java  package选项,必须重装jdk和MATLAB,这个build的过程需要花挺长时间的。



         到这里会发现左边会生成一个Untitle1文件夹以及Untitle1.prj文件,打开这个文件夹到下面的src目录会有一个Untitle.jar,将这个复制到我们的java工程,导入之后就可以使用了。

         好吧,现在我们来新建java工程,这里小编打算将这个人脸识别程序使用java web实现,因为这个学期学了一点java web的知识,学以致用嘛。对java web不熟悉的同学可以使用swing图形界面完成,更简单。小编的java web程序只有一个主界面,后端使用了strut2框架和spring框架。主页样式如下:



        喔,小编这里忘了,这里小编将代码改了一点点,就是训练集使用了实训老师给的六个人的图片,有些人的图片不足十张,小编复制粘贴凑够了十张。还有一点就是,这里对图片的格式有严格的要求,就是必须是112*92像素而且为位深度为8的人脸图片,不然其他的图片识别不出来,小编不会使用java对图片进行修改,要不然的话本来是想做到Android应用去的。记住了,要使用自己的训练集的话就得将MATLAB程序中40个人改为自己的训练集个数,这里小编就不做讲解了,自己琢磨代码。

         在java web程序中,用户需要将图片上传到服务器,服务器对图片进行人脸识别处理,然后将识别结果以及结果的第一张图片返回浏览器共用户对比,本来是想使用Ajax技术实现的,无奈不会使用Ajax上传文件,这里是以刷新的方式进行传递数据的。

用户上传图片部分代码(从网上找的):

<script>
	var isleagel = false;
	function upload() {
		var docObj = document.getElementById("doc");

		var imgObjPreview = document.getElementById("preview");
		if (docObj.files && docObj.files[0]) {
			if (!isImage(docObj.value)) {
				alert("请选择正确的图片格式!");
				isleagel = false;
				return;
			}
			isleagel = true;
			//火狐下,直接设img属性
			imgObjPreview.style.display = 'block';
			imgObjPreview.style.width = '92';
			imgObjPreview.style.height = '112';
			//imgObjPreview.src = docObj.files[0].getAsDataURL();
			//火狐7以上版本不能用上面的getAsDataURL()方式获取,需要一下方式
			imgObjPreview.src = window.URL.createObjectURL(docObj.files[0]);

		} else {
			//IE下,使用滤镜
			isleagel = true;
			docObj.select();
			var imgSrc = document.selection.createRange().text;
			var localImagId = document.getElementById("localImag");
			//必须设置初始大小
			//localImagId.style.width = "92";
			//localImagId.style.height = "112";
			//图片异常的捕捉,防止用户修改后缀来伪造图片
			try {
				localImagId.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)";
				localImagId.filters
						.item("DXImageTransform.Microsoft.AlphaImageLoader").src = imgSrc;
			} catch (e) {
				isleagel = false;
				alert("您上传的图片格式不正确,请重新选择!");
				return false;
			}
			imgObjPreview.style.display = 'none';
			document.selection.empty();
		}
		return true;
	}
	function isImage(str) {
		var b = /\w+([.jpg|.png|.gif|.swf|.bmp|.jpeg]){1}$/;
		var t_value = str.toLowerCase();
		var a = b.test(t_value);
		return a;
	}
	function faceMatching() {
		if (!isleagel) {
			alert("您上传的图片格式不正确,请重新选择!");
			return;
		}

		var form = document.getElementById('form');
		form.action = "${pageContext.request.contextPath}/register";
		form.submit();
	}

后端对用户上传的图片进行识别处理的代码:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class RegisterServlet extends HttpServlet {

	public RegisterServlet() {
		super();
	}

	public void destroy() {
		super.destroy(); // Just puts "destroy" string in log
		// Put your code here
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		int i = -1;
		String savePath = this.getServletContext().getRealPath(
				"/upload");
		File file = new File(savePath);
		// 判断上传文件的保存目录是否存在
		if (!file.exists() && !file.isDirectory()) {
			System.out.println(savePath + "目录不存在,需要创建");
			// 创建目录
			file.mkdir();
		}
		// 消息提示
		String message = "";
		try {
			// 使用Apache文件上传组件处理文件上传步骤:
			// 1、创建一个DiskFileItemFactory工厂
			DiskFileItemFactory factory = new DiskFileItemFactory();
			// 2、创建一个文件上传解析器
			ServletFileUpload upload = new ServletFileUpload(factory);
			// 解决上传文件名的中文乱码
			upload.setHeaderEncoding("UTF-8");
			// 3、判断提交上来的数据是否是上传表单的数据
			if (!ServletFileUpload.isMultipartContent(request)) {
				// 按照传统方式获取数据
				return;
			}
			// 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
			List<FileItem> list = upload.parseRequest(request);
			for (FileItem item : list) {
				// 如果fileitem中封装的是普通输入项的数据
				if (item.isFormField()) {
					String name = item.getFieldName();
					// 解决普通输入项的数据的中文乱码问题
					String value = item.getString("UTF-8");
					// value = new String(value.getBytes("iso8859-1"),"UTF-8");
					System.out.println(name + "=" + value);
				} else {// 如果fileitem中封装的是上传文件
						// 得到上传的文件名称,
					String filename = item.getName();
					System.out.println(filename);
					if (filename == null || filename.trim().equals("")) {
						continue;
					}
					// 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:
					// c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
					// 处理获取到的上传文件的文件名的路径部分,只保留文件名部分
					filename = filename
							.substring(filename.lastIndexOf("\\") + 1);
					// 获取item中的上传文件的输入流
					InputStream in = item.getInputStream();
					// 创建一个文件输出流
					FileOutputStream out = new FileOutputStream(savePath + "\\"
							+ "recognise.bmp");
					// 创建一个缓冲区
					byte buffer[] = new byte[1024];
					// 判断输入流中的数据是否已经读完的标识
					int len = 0;
					// 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
					while ((len = in.read(buffer)) > 0) {
						// 使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\"
						// + filename)当中
						out.write(buffer, 0, len);
					}
					// 关闭输入流
					in.close();
					// 关闭输出流
					out.close();
					// 删除处理文件上传时生成的临时文件
					item.delete();

					i = RecogniseUtil.faceMatching(savePath + "\\"
							+ "recognise.bmp");
					message = "文件上传成功!识别结果为第:" + i + "个人.";
				}
			}
		} catch (Exception e) {
			message = "文件上传失败!";
			e.printStackTrace();

		}

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("oldimage", "recognise.bmp");
		map.put("newimage", i + "\\1.bmp");

		request.setAttribute("message", message);
		request.setAttribute("map", map);
		request.getRequestDispatcher("/index.jsp").forward(request, response);
	}

	public void init() throws ServletException {
		// Put your code here
	}

}

       这里小编还写了一个工具类,里面有两个方法,一个是获取指定路径的图片,以Image的形式返回,第二个是人脸匹配调用函数,代码如下:

import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import myrecognise.myrecognise;

public class RecogniseUtil {

	public static Image getSimpleImage(String path) {
		// TODO Auto-generated method stub
		Image image = null;
		try {
			image = ImageIO.read(new File(path));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return image;
	}

	public static int faceMatching(String string) {
		// TODO Auto-generated method stub
		myrecognise recognise = null;
		Object[] result = null;
		int num = -1;
		try {
			recognise = new myrecognise();
			result = recognise.Recg(1, string);
			num = Integer.parseInt(result[0].toString());
			return num;
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		return num;
	}

}


源码下载地址:java+MATLAB在web上面实现人脸匹配demo












评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值