世上本没有调包侠,调的越来越熟练也就成了调包侠…
最近学校的项目要做个人脸表情识别的app,但是!我作为一个菜鸟,什么都不会,我就跑去问老师,能不能慢慢?
老师一听,什么???慢慢来???你在逗我吗???
在各种威逼利诱之下,无奈只能硬着头皮接了下来。搜遍了各种资料,发现其实还是蛮好做的这个上手好难啊。
在搜集了各种资料之后,先写了一个基于Java的小demo来实验一下,没想到就成了。
话不多说,有图有真相:
就算是一个小demo也是要有设计的灵魂的:
先大致理一下思路,首先,要有包可以调。搜了一下,发现除了各大公司的API以外,能比较适合新手的包也就剩下opencv了
opencv 的Java包还是蛮好装的,去官网下一个安装包就可以了,为了省事,就直接用最新版了。
首先下载Windows下的安装包。
下载之后随便找个地方安装:
上面就是安装完了的样子。
然后就是配置的事情了,下面放出来eclipse下配置的方式:
首先新建一个项目,然后在项目的图标上右键选择Properties,然后点开Build Path,
选择Add External JARS,打开你安装opencv的文件夹,一直点到如下图:
选择jar包,然后打开,之后点击JRE System Library,选择Native Library Location ,双击打开
点击External Folder ,一直点到刚才找到jre包的位置,根据自己的机器来选取环境,64位的电脑就选取x64,点击ok
这样,opencv的环境配置就算完成了。
首先试着建一个Mat对象看看:
import org.opencv.core.Core;
import org.opencv.core.Mat;
public class example {
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);//这个一定要在程序运行前使用,这条语句规定了代码编译时候使用的库
Mat m =new Mat();
System.out.println(m);
}
}
运行一下试试看:
Mat [ 0*0*CV_8UC1, isCont=false, isSubmat=false, nativeObj=0x766840, dataAddr=0x0 ]
Process finished with exit code 0
打印出来了如下的消息,说明opencv的配置是成功的。
接下来就是实现人脸识别的过程了。
俗话说得好,天下代码一大抄,看你会抄不会抄。
我这个人又菜又懒自然就只能选择抄代码了。但是抄并不是没灵魂的Ctrl+c和Ctrl+v,抄的时候也要思考一下这个是干什么的呀。
闲话就先说到这里,先看看如何导入一张图片吧。
首先写一个导入图片的函数
/**
*导入图片
* @param add
* @return 一个bufferedImage
*/
public static BufferedImage loadImage(String add){
try {
BufferedImage img= ImageIO.read(new File(add));
return img;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
根据图片地址导入之后,返回一个BufferedImage。
因为opencv处理图象的基本类是Mat,所以需要将得到的图片转换为Mat
为了后面省事就顺便把Mat转BufferedImage的方法也给出来:
/**
* BufferedImage转换成Mat
*
* @param src
* 要转换的BufferedImage
*
*/
public static Mat bufImg2Mat(BufferedImage src) {
if(src.getType() != BufferedImage.TYPE_3BYTE_BGR) {
BufferedImage image = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
image.getGraphics().drawImage(src, 0, 0, null);
src = image;
}
WritableRaster raster = src.getRaster();
DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
byte[] pixels = buffer.getData();
Mat mat = Mat.eye(src.getHeight(), src.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, pixels);
return mat;
}
/**
* Mat转换成BufferedImage
*
* @param matrix
* 要转换的Mat
* @param fileExtension
* 格式为 ".jpg", ".png", etc
* @return
*/
public static BufferedImage mat2BI(Mat mat){
int dataSize =mat.cols()*mat.rows()*(int)mat.elemSize();
byte[] data=new byte[dataSize];
mat.get(0, 0,data);
int type=mat.channels()==1?
BufferedImage.TYPE_BYTE_GRAY:BufferedImage.TYPE_3BYTE_BGR;
if(type==BufferedImage.TYPE_3BYTE_BGR){
for(int i=0;i
写好了这两个方法之后就可以开工了,建立一个级联分类器,然后根据分类器返回的数据就可以在图片上把人脸标记出来了,是不是很简单啊?为了代码好看,先把检测人脸的方法封装成一个函数
public static Mat detectFace(Mat mat_img) {
System.out.println("Running DetectFace ... ");
// 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,该文件位于opencv安装目录中
CascadeClassifier faceDetector = new CascadeClassifier("E:\\Java\\Libs\\opencv_4.0.1\\opencv\\sources\\data\\haarcascades\\"
+ "haarcascade_frontalface_alt2.xml");
// 在图片中检测人脸
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(mat_img, faceDetections);
Rect[] rects = faceDetections.toArray();
if(rects != null && rects.length >= 1){
for (Rect rect : rects) {
System.out.println(rect);
Imgproc.rectangle(mat_img, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(0, 0, 255), 2);
}
}
通过这个方法返回了一个将人脸框出来的Mat图象,最后我们要做的就是将这个图象转化为BufferedImage然后用一个图像界面显示出来。
最后的代码如下:
public static void main(String[] args) {
String address="C:\\Users\\imwxc\\Desktop\\timg1.jpg";
try {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//创建一个mat
Mat img_mat=new Mat();
img_mat= M2I.bufImg2Mat(loadImage(address));
img_mat=detectFace(img_mat);//检测人脸
BufferedImage img2paint=M2I.mat2BI(img_mat);
featureDecter.frame f=new featureDecter.frame();
f.setSize(img2paint.getWidth()+200,img2paint.getHeight()+200);
f.draw(img2paint);
}catch (Exception e){
e.printStackTrace();
}
}
这里用了一个JFrame 顺便也给出来:
public static class frame extends JFrame {
frame(){
this.setDefaultCloseOperation(3);
this.setBounds(0,0,800,800);
this.setSize(800,800);
this.setVisible(true);
}
public void draw(BufferedImage img){
while(true){
this.getGraphics().drawImage(img,100,100,img.getWidth(),img.getHeight(),null);
}
}
}
最后的最后就是开篇的那个效果图