首先感谢虹软,是你们提供这么好的SDK支撑了我们的想象力!
这是一个用javav编写的可视化应用,用户通过自己的脸和计算机进行交互,计算机则通过萌萌女孩的语音和用户对话。
核心程序就是利用ArcFace2.0识别性别、年龄,但是为了获得正面脸,会根据ArcFace2.0的人脸3D角度、用语音提醒用户,这是一个的互动环节。最后,程序会幽默的、萌萌的告诉用户他的性别、年龄。
获取SDK 请戳这里
完整的项目源码、可执行程序,放在百度网盘:链接: https://pan.baidu.com/s/1eHF66l111S3Rs0VaS7v_LA
提取码: ffag
其中主要的3个java文件,代码如下:
===================================== HowOldAreU.java ===================================== package app; import java.awt.EventQueue; import javax.swing.JFrame; import java.io.File; import java.io.FileNotFoundException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.awt.BorderLayout; import com.alibaba.fastjson.JSONArray; import com.arcsoft.face.FaceEngine; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamPanel; import tools.MyFunc; import javax.swing.JOptionPane; /*这是一个用javav编写的可视化应用,用户通过自己的脸和计算机进行交互,计算机则通过萌萌女孩的语音和用户对话。 核心程序就是利用ArcFace2.0识别性别、年龄,但是为了获得正面脸,会根据ArcFace2.0的人脸3D角度、用语音提醒用户,这是一个的互动环节。 最后,程序会幽默的、萌萌的告诉用户他的性别、年龄。 作者:huanghua8080@126.com */ public class HowOldAreU { //应用根目录 public static String fs = File.separator; public final static String localPath = System.getProperty("user.dir")+fs; public final static String soundDir = localPath+"sound"+fs; // public static Webcam camera = null; private JFrame frame; // public static FaceEngine faceEngine = null; @SuppressWarnings("rawtypes") public static List FaceFeature = new ArrayList<Map<String, String>>(); public static JSONArray aryFFTime = new JSONArray(); public static JSONArray aryFFCnt = new JSONArray(); public static String lastTime = "2019-01-09 13:30:00"; public static int faceCnt = 0; /** * Launch the application. */ public static void main(String[] args) { //判断程序是否已经运行 String s = localPath+"lockApp.txt"; // RandomAccessFile raf = null; try { raf = new RandomAccessFile(new File(s), "rws"); } catch (FileNotFoundException e1) { JOptionPane.showMessageDialog(null, "独占文件时发生异常。"+e1, "错误",JOptionPane.ERROR_MESSAGE); System.exit(0); } FileChannel fcin = raf.getChannel(); FileLock flin = null; try { flin = fcin.tryLock(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "锁文件时发生异常:"+e, "错误",JOptionPane.ERROR_MESSAGE); System.exit(0); } if (flin == null) { JOptionPane.showMessageDialog(null, "程序已在运行,不可重复。", "错误",JOptionPane.ERROR_MESSAGE); System.exit(0); } s = "D:\Dev\ec_workspace\cs1914age"; if(!s.equals(System.getProperty("user.dir"))) { if(args.length == 0) { JOptionPane.showMessageDialog(null, "没有入参,程序将终止。", "错误",JOptionPane.ERROR_MESSAGE); System.exit(0); return; } if(!"age".equals(MyFunc.strTrim(args[0]).toLowerCase())) { JOptionPane.showMessageDialog(null, "入参错误,程序将终止。", "错误",JOptionPane.ERROR_MESSAGE); System.exit(0); return; } } //获取摄像头 camera = Webcam.getDefault(); if (camera == null) { JOptionPane.showMessageDialog(null, "摄像头获取失败。", "错误",JOptionPane.ERROR_MESSAGE); return; } //初始化人脸引擎 s = HowOldAreUAs.initEngine(); if(!"".equals(s)) { JOptionPane.showMessageDialog(null, s, "错误",JOptionPane.ERROR_MESSAGE); System.exit(0); return; } //启动窗体 EventQueue.invokeLater(new Runnable() { public void run() { try { HowOldAreU window = new HowOldAreU(); window.frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the application. */ public HowOldAreU() { initialize(); } /** * Initialize the contents of the frame. */ private void initialize() { // frame = new JFrame(); frame.setTitle("猜年龄"); frame.setBounds(100, 100, 610, 370); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setLayout(new BorderLayout(0, 0)); frame.setExtendedState(JFrame.MAXIMIZED_BOTH); frame.setUndecorated(true);//去边框 //摄像头加载到面板 WebcamPanel panel = new WebcamPanel(camera); frame.getContentPane().add(panel, BorderLayout.CENTER); //启动声音 HowOldAreUAs.playSound(100); //线程(识别频率:毫秒) Timer timerMain = new Timer(); timerMain.scheduleAtFixedRate(new TimerTask() { public void run() { if (camera != null) { HowOldAreUAs.photo(); } } }, 0, 500); } }
======================
HowOldAreUAs
====================================
package app;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.Face3DAngle;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.FunctionConfiguration;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.Rect;
import com.arcsoft.face.enums.ImageFormat;
import com.sun.jna.Platform;
import app.FaceAbout.ImageInfo;
import tools.MyFunc;
import tools.SoundPlay;
public class HowOldAreUAs {
public static final int recoFreq = 60;//同一人不重复识别时间(秒)
public static final int scoreThreshold = 70;//人脸相似度阀值
//3D角度阀值
public static final BigDecimal yes3d = new BigDecimal("5");
//拍照
@SuppressWarnings("unchecked")
public static void photo() {
int rtn=-1,sex=-1,age=-1;
//当前时间
String nowTime = MyFunc.getSvrTime("yyyy-MM-dd HH:mm:ss");
//不重复识别时间(去除过期的)
for(int n=HowOldAreU.aryFFTime.size()-1;n>=0;n--) {
if(MyFunc.datetimeSub(HowOldAreU.aryFFTime.get(n).toString(), nowTime) >= recoFreq) {
HowOldAreU.aryFFTime.remove(n);
HowOldAreU.aryFFCnt.remove(n);
HowOldAreU.FaceFeature.remove(n);
}
}
//拍照
BufferedImage cameraImg = HowOldAreU.camera.getImage();
//找脸
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
ImageInfo imageInfo = new FaceAbout().bufferedImage2ImageInfo(cameraImg);
HowOldAreU.faceEngine.detectFaces(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
int cnt = faceInfoList.size();
if (cnt == 0) {
//5分钟后,如果没有人来,则呼唤
if(MyFunc.datetimeSub(HowOldAreU.lastTime, nowTime) > 300) {
HowOldAreU.lastTime = nowTime;
playSound(200);
}
return;
}
HowOldAreU.lastTime = nowTime;
//找最大脸(第一张脸即为最大脸)
FaceInfo oneFace = faceInfoList.get(0);
//提取脸纹
FaceFeature CmFeature = new FaceFeature();
rtn = HowOldAreU.faceEngine.extractFaceFeature(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(),
ImageFormat.CP_PAF_BGR24, oneFace, CmFeature);
if (rtn != 0) {
playSound(250);
return;
}
//是否刚刚识别过
int rfe = 0;
int dSimilScore = 0;
FaceSimilar faceSimilar = new FaceSimilar();
for(int n=0;n<HowOldAreU.aryFFTime.size();n++) {
rtn = HowOldAreU.faceEngine.compareFaceFeature(CmFeature, (FaceFeature) HowOldAreU.FaceFeature.get(n), faceSimilar);
if (rtn != 0) {
return;
}
//得分
dSimilScore = new BigDecimal(faceSimilar.getScore()).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
//大于阀值
if(dSimilScore >= scoreThreshold){
if(MyFunc.datetimeSub(HowOldAreU.aryFFTime.get(n).toString(), nowTime) < recoFreq) {
rfe = 1;
int hdt = Integer.parseInt( HowOldAreU.aryFFCnt.get(n).toString() );
if(hdt >= 1 && hdt <= 3) {
playSound(180+hdt);
HowOldAreU.aryFFCnt.set(n, hdt+1 );
//停顿一下
try {
Thread.sleep(3000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
break;
}
}
}
//最近识别过
if(rfe == 1) {return;}
//识别过10个人后,做一次自我介绍
if(HowOldAreU.faceCnt == 11) {
HowOldAreU.faceCnt = 0;
}
if(HowOldAreU.faceCnt == 0) {
playSound(150);
HowOldAreU.faceCnt ++;
}
//停顿一下
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
//原型
faceInfoList.add(oneFace);
rtn = HowOldAreU.faceEngine.process(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(),
ImageFormat.CP_PAF_BGR24, faceInfoList,
FunctionConfiguration.builder().supportAge(true).supportFace3dAngle(true).supportGender(true).build());
if (rtn != 0) {
playSound(250);
return;
}
//3D信息提取
List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
rtn = HowOldAreU.faceEngine.getFace3DAngle(face3DAngleList);
if (rtn != 0) {
playSound(250);
return;
}
if(face3DAngleList.size() == 0) {
playSound(250);
return;
}
//0: 正常,其他数值:检测结果不可信
int status3d = face3DAngleList.get(0).getStatus();
if(status3d != 0) {return;}
BigDecimal pitch = new BigDecimal("0");
BigDecimal roll = new BigDecimal("0");
BigDecimal yaw = new BigDecimal("0");
BigDecimal yes3db = new BigDecimal("0").subtract(yes3d);
//俯仰角
pitch = new BigDecimal(face3DAngleList.get(0).getPitch()).setScale(7, BigDecimal.ROUND_HALF_UP);
if(pitch.compareTo(yes3d) == 1) {
playSound(301);
return;
}
if(pitch.compareTo(yes3db) == -1) {
playSound(302);
return;
}
//横滚角
roll = new BigDecimal(face3DAngleList.get(0).getRoll()).setScale(7, BigDecimal.ROUND_HALF_UP);
if(roll.compareTo(yes3d) == 1) {
playSound(311);
return;
}
if(roll.compareTo(yes3db) == -1) {
playSound(312);
return;
}
//偏航角
yaw = new BigDecimal(face3DAngleList.get(0).getYaw()).setScale(7, BigDecimal.ROUND_HALF_UP);
if(yaw.compareTo(yes3d) == 1) {
playSound(321);
return;
}
if(yaw.compareTo(yes3db) == -1) {
playSound(322);
return;
}
//年龄提取
List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
rtn = HowOldAreU.faceEngine.getAge(ageInfoList);
if (rtn != 0) {
playSoundSexAge(-1,-1);
return;
}
age = ageInfoList.get(0).getAge();
if(age > 120) {age = 120;}
//性别提取
List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
rtn = HowOldAreU.faceEngine.getGender(genderInfoList);
if (rtn != 0) {
playSoundSexAge(-1,age);
return;
}
sex = genderInfoList.get(0).getGender();
//
if(sex == -1 && age == -1) {
playSound(360);
return;
}
//播报
playSoundSexAge(sex,age);
//记录人脸,防止重复识别同一个人
HowOldAreU.FaceFeature.add(CmFeature);
HowOldAreU.aryFFTime.add(nowTime);
HowOldAreU.aryFFCnt.add("1");
//记录已识别数量
HowOldAreU.faceCnt ++;
//System.out.println(HowOldAreU.faceCnt+" "+now_time);
}
public static void playSoundSexAge(int sex,int age) {
//不同年龄段,不同称谓
String agename = "frend";
if(sex >= 0) {
if(age >= 0 && age <= 2) {
agename = "00-"+sex;
}else if(age >= 3 && age <= 18) {
agename = "03-"+sex;
}else if(age >= 19 && age <= 45) {
agename = "19-"+sex;
}else if(age >= 46 && age <= 75) {
agename = "46-"+sex;
}else if(age >= 76 && age <= 120) {
agename = "76-"+sex;
}
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+"agename"+HowOldAreU.fs+"agename-"+agename+".mp3", null);
//推测用语
JSONArray ary = new JSONArray();
ary.add("401");//你,大概
ary.add("402");//我猜你
ary.add("403");//我估计你
ary.add("404");//我看你
ary.add("405");//你看起来
int cnt = ary.size();
//随机选择一个
int idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
//年龄
SoundPlay.playSoundFile(HowOldAreU.soundDir+"age"+HowOldAreU.fs+"age"+age+".mp3", null);
//停顿一下
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
//确认
ary = new JSONArray();
ary.add("481");//对不对啊?
ary.add("482");//是不是呢?
ary.add("483");//准吗?
ary.add("484");//差不多吗?
ary.add("485");//靠谱吧?
cnt = ary.size();
idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
//停顿一下
try {
Thread.sleep(2000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
//笑一个
ary = new JSONArray();
ary.add("501");//哈哈!
ary.add("502");//嘻嘻!
cnt = ary.size();
idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
//如果错了
ary = new JSONArray();
ary.add("521");//如果我说错了,
ary.add("522");//要是我没有说对,
cnt = ary.size();
idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
//别生气
ary = new JSONArray();
ary.add("541");//你可别生气哦!
ary.add("542");//你别往心里去啊!
ary.add("543");//你千万别介意哈!
cnt = ary.size();
idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
//停顿一下
try {
Thread.sleep(2000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
//下一个
ary = new JSONArray();
ary.add("561");//来,下一个!
ary.add("562");//请下一位朋友!
ary.add("563");//下一位,谁来?
cnt = ary.size();
idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
}
public static void playSound(int sound_kind) {
// http://ai.baidu.com/tech/speech/tts
JSONArray ary = new JSONArray();
//文件集
switch(sound_kind) {
case 100:
ary.add("101");//秋语已经启动,就要工作啦!
break;
case 150:
//大家好,我是机器人。主人给我取名:秋语,他还帮我训练了一双火眼金睛,
//看一眼就能识别出你们人类的性别和年龄。有人想过来试一试吗?
ary.add("150");
break;
case 181:
ary.add("181");//你来过的,一分钟之后再来,好吗?
break;
case 182:
ary.add("182");//你来过的,一分钟之后再来,好吗?
break;
case 183:
ary.add("183");//你怎么还来呀?跟你说了等一分钟的!你真是个急性子,不理你了。
break;
case 200: //没有发现人脸时
ary.add("201");//怎么没有人来跟我玩儿?
ary.add("202");//有人吗?快来和我玩啦!
ary.add("203");//我知道你几岁了,过来试试吧!
ary.add("204");//你们人呢?都到哪儿去了?
ary.add("205");//我等了老半天,怎么连个人影也没看到!
break;
case 250: //看不请人脸或无法提取脸纹时
ary.add("251");//嗨!靠近一点儿,我想看看你呢!
ary.add("252");//喂!过来一点嘛,我都看不清你!
ary.add("253");//hello,离我近一点儿,会有惊喜的!
break;
//3D角度过大
case 301://俯仰角过大:请低一下头!
case 302://俯仰角过大:把头抬一下!
case 311://横滚角过大:头向左转一下!
case 312://横滚角过大:向右转一下头!
case 321://偏航角过大:脖子向左歪一下!
case 322://偏航角过大:向右歪一下脖子!
ary.add(sound_kind);
break;
case 360: //性别、年龄均未知
ary.add("361");//你太神秘了,我实在猜不出你几岁!
ary.add("362");//你到底几岁呢?我绞尽脑汁也想不出来!
ary.add("363");//我无法识别你的年龄,我要请主人继续进化我。
break;
}
int cnt = ary.size();
if(cnt == 0) {return;}
//随机选择一个
int idx = 0;
if(cnt > 1) {
Random random = new Random();
idx = random.nextInt(cnt)%(cnt+1);
}
//播放
SoundPlay.playSoundFile(HowOldAreU.soundDir+ary.get(idx)+".mp3", null);
}
public static String initEngine() {
JSONObject parm = MyFunc.GetAllProperties("config/parm.properties");
String s = MyFunc.strTrim(parm.getString("err"));
if(!"".equals(s)) {return "参数文件读取失败。"+s; }
//APPID
String APPID = MyFunc.strTrim(parm.getString("APP_ID"));
if("".equals(APPID)){return "终端APPID缺失,程序将终止。";}
//SDKKEY
String WIN_SDKKEY = MyFunc.strTrim(parm.getString("WIN_SDKKEY"));
String LIN_SDKKEY = MyFunc.strTrim(parm.getString("LIN_SDKKEY"));
String SDKKEY = WIN_SDKKEY;
if(!Platform.isWindows()) {SDKKEY = LIN_SDKKEY;}
if("".equals(SDKKEY)){return "终端SDKKEY缺失,程序将终止。";}
//加载动态库
s = FaceAbout.loadDllSo();
if(!"".equals(s)) {
return "动态库加载失败,程序将终止。"+s;
}
//人脸引擎初始化
try {
HowOldAreU.faceEngine =