首先感谢虹软,是你们提供这么好的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 =