技术关键点如下:
1、智能终端通过拍照获取人脸图片信息,然后与服务器中预先录入的人脸进行比对。
2、比对结果通过json字符集返回到智能终端,json字符集中confidence字段(注:人脸比对结果置信度,即两张人脸比对的相似度)通过数据类型转换后(注:数据类型转换参考谭浩强《C程序设计(第四版)》,各计算机编程语言可能会有所不同),获取其双精度浮点型数据,如果该值大于90.000,智能终端会自动打开一个webview页面,并发送信号给硬件控制端,硬件控制端接收信号后,锁会弹开。
3、出于安全性及防盗考虑,智能终端拍照获取完人脸图片,在进行人脸识别比对的同时,亦会将拍照获取到的每一张人脸信息上传到服务器保存下来(注:包括每次上传的时间),如果是双胞胎,或者本系统被3d打印头套**等,此举能极大的增加安全性,毕竟所有的匹配图片都能在事后查得到,现在云服务器也特别便宜。
4、智能终端(注:此处指移动终端,比如手机等,非定点悬挂终端)也可以实现远程开锁,比如家里来客人了,人却在外面,此时通过移动智能终端上的app,可实现远程开锁。
我给远程控制模块刷了两套程序,一套本地局域网快速识别通过的,一套需点击通过验证的,当然,个人认为本地局域网掣肘颇多,很多方面都不如云端远程认证好,但方便快捷。
最后,正在考虑帮一个做智慧餐厅的小伙伴做一套人脸识别系统检测分析客户的回头率等数据。
放几张效果图吧;
识别通过后,会进入以下界面:
通过网络遥控,可以实现开锁,硬件用的芯片是Nodemcu,开发语言是Lua,挺简单的一门语言,我之前也做过一年硬件开发,自动化啥的,也实现过红外、蓝牙等硬件端的数据传输控制等,后续会再说一说的,这个开锁可以设置一个密码组,我这里为了方便,设置的只是一个按钮。
安卓前端代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#151d34"
tools:context="com.example.admin.mywrite.MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="center"
android:textColor="#FFF"
android:text="人脸门禁系统,欢迎您!"
/>
<ImageView
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:layout_marginTop="-30dp"
android:id="@+id/image_header"
android:src="@drawable/e2"
/>
<Button
android:layout_width="200dp"
android:layout_height="50dp"
android:textColor="#fff"
android:layout_marginTop="30dp"
android:layout_gravity="center"
android:text="刷脸开门"
android:id="@+id/btn_takephoto"
android:background="@drawable/e1"
/>
<Button
android:layout_width="200dp"
android:layout_height="50dp"
android:textColor="#fff"
android:layout_marginTop="30dp"
android:layout_gravity="center"
android:text="选取相册图片"
android:id="@+id/btn_selectimage"
android:background="@drawable/e1"
/>
<Button
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_marginTop="30dp"
android:textColor="#fff"
android:onClick="subTo"
android:text="人脸情绪检测"
android:id="@+id/a"
android:background="@drawable/e1"
/>
<TextView
android:text="TextView"
android:layout_width="fill_parent"
android:layout_height="150dp"
android:scrollbars="vertical"
android:id="@+id/textView" />
</LinearLayout>
</ScrollView>
</RelativeLayout>
然后是.java:
package com.example.admin.mywrite;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.megvii.cloud.http.CommonOperate;
import com.megvii.cloud.http.FaceSetOperate;
import com.megvii.cloud.http.Response;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import cn.bmob.v3.Bmob;
import cn.bmob.v3.BmobInstallation;
import cn.bmob.v3.datatype.BmobFile;
import cn.bmob.v3.exception.BmobException;
import cn.bmob.v3.listener.SaveListener;
import cn.bmob.v3.listener.UploadFileListener;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int IMAGE_REQUEST_CODE = 0;
private static final int CAMERA_REQUEST_CODE = 1;
private static final int RESIZE_REQUEST_CODE = 2;
private static final String IMAGE_FILE_NAME = "header.jpg";
private ImageView mImageHeader;
TextView mTextView;
String key = "1LyI8ww5BX5n0rV6VzFnEsomMpqHknzz";//api_key
String secret = "8SJ1sYPMrnUDaqU5jmc7dqo8zNdX7HHq";//api_secret
String imageUrl = "http://bmob-cdn-14444.b0.upaiyun.com/2017/11/15/7e73934440c01f2880588492268b1854.jpg";
StringBuffer sb = new StringBuffer();
Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
Bmob.initialize(this,"126310d4d413da5186aab94be89b4253");
BmobInstallation.getCurrentInstallation().save();
setupViews();
}
private void setupViews() {
mImageHeader = (ImageView) findViewById(R.id.image_header);
final Button selectBtn1 = (Button) findViewById(R.id.btn_selectimage);
final Button selectBtn2 = (Button) findViewById(R.id.btn_takephoto);
mTextView=(TextView)findViewById(R.id.textView);
mTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
selectBtn1.setOnClickListener(this);
selectBtn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_selectimage:
Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.addCategory(Intent.CATEGORY_OPENABLE);
galleryIntent.setType("image/*");//图片
startActivityForResult(galleryIntent, IMAGE_REQUEST_CODE);
break;
case R.id.btn_takephoto:
if (isSdcardExisting()) {
Intent cameraIntent = new Intent(
"android.media.action.IMAGE_CAPTURE");//拍照
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, getImageUri());
cameraIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE);
} else {
Toast.makeText(v.getContext(), "请插入sd卡", Toast.LENGTH_LONG)
.show();
}
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
} else {
switch (requestCode) {
case IMAGE_REQUEST_CODE:
Uri originalUri=data.getData();//获取图片uri
resizeImage(originalUri);
//以下方法将获取的uri转为String类型哦。
String []imgs1={MediaStore.Images.Media.DATA};//将图片URI转换成存储路径
Cursor cursor=this.managedQuery(originalUri, imgs1, null, null, null);
int index=cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
final String img_url=cursor.getString(index);
// upload(img_url);
// mTextView.setText(img_url);
//showToast(img_url);
break;
case CAMERA_REQUEST_CODE:
if (isSdcardExisting()) {
resizeImage(getImageUri());
String []imgs={MediaStore.Images.Media.DATA};//将图片URI转换成存储路径
Cursor cursor1=this.managedQuery(getImageUri(), imgs, null, null, null);
int index1=cursor1.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor1.moveToFirst();
final String img_url1=cursor1.getString(index1);
upload(img_url1);
// mTextView.setText(img_url1);
//showToast(img_url1);
} else {
Toast.makeText(MainActivity.this, "未找到存储卡,无法存储照片!",
Toast.LENGTH_LONG).show();
}
break;
case RESIZE_REQUEST_CODE:
if (data != null) {
showResizeImage(data);
}
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void showToast(String msg){
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
private boolean isSdcardExisting() {//推断SD卡是否存在
final String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
return true;
} else {
return false;
}
}
private byte[] getBitmap(int res){
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), res);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
public void subTo(View view){
Intent intent1=new Intent(MainActivity.this,Main3Activity.class);
this.startActivity(intent1);
}
private String getFaceToken(Response response) throws JSONException {
if(response.getStatus() != 200){
return new String(response.getContent());
}
String res = new String(response.getContent());
//Log.e("response", res);
JSONObject json = new JSONObject(res);
String faceToken = json.optJSONArray("faces").optJSONObject(0).optString("face_token");
return faceToken;
}
public static String bitmapToBase64(Bitmap bitmap) {
String result = null;
ByteArrayOutputStream baos = null;
try {
if (bitmap != null) {
baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
baos.flush();
baos.close();
byte[] bitmapBytes = baos.toByteArray();
result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (baos != null) {
baos.flush();
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public void resizeImage(Uri uri) {//重塑图片大小
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");//能够裁剪
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
intent.putExtra("return-data", true);
startActivityForResult(intent, RESIZE_REQUEST_CODE);
}
private void showResizeImage(Intent data) {//显示图片
Bundle extras = data.getExtras();
if (extras != null) {
Bitmap photo = extras.getParcelable("data");
String image = "http://bmob-cdn-14444.b0.upaiyun.com/2017/11/15/7e73934440c01f2880588492268b1854.jpg";
sub(photo,image);
Drawable drawable = new BitmapDrawable(photo);
mImageHeader.setImageDrawable(drawable);
}
}
private Uri getImageUri() {//获取路径
return Uri.fromFile(new File(Environment.getExternalStorageDirectory(),
IMAGE_FILE_NAME));
}
public void sub(Bitmap bitmap, final String imagesub){
final String base=bitmapToBase64(bitmap);
new Thread(new Runnable() {
@Override
public void run() {
CommonOperate commonOperate = new CommonOperate(key, secret, false);
FaceSetOperate FaceSet = new FaceSetOperate(key, secret, false);
ArrayList<String> faces = new ArrayList<>();
//拿到人脸的详细位置信息
try {
//检测第二个人脸,传的是本地图片文件
//detect first face by local file
String attrs = new String();
attrs = "emotion,beauty,ethnicity,facequality,skinstatus";
Response response1 = commonOperate.detectBase64(base, 0, attrs);
String faceToken1 = getFaceToken(response1);
final String res1 = new String(response1.getContent());
final String str1 = res1.substring(291, 300).trim();
String str6=res1.substring(res1.indexOf("{",res1.indexOf("{")+1), res1.indexOf("}",res1.indexOf("facequality"))).trim();
String a[]=str6.split(":");
String a0=a[0].substring(2,12);
String a1=a[1].substring(3,10);
String a2=a[2].substring(3,10);
String sa1[]=a[3].split(",");
String b1=sa1[0];
String a3=sa1[1].substring(2,9);
String sa2[]=a[4].split(",");
String b2=sa2[0];
String a4=sa2[1].substring(2,9);
String sa3[]=a[5].split(",");
String b3=sa3[0];
String a5=sa3[1].substring(2,7);
String sa4[]=a[6].split(",");
String b4=sa4[0];
String a6=sa4[1].substring(2,10);
String sa5[]=a[7].split(",");
String b5=sa5[0];
String a7=sa5[1].substring(2,6);
String sa6[]=a[8].split(",");
String b6=sa6[0];
String a8=sa6[1].substring(2,11);
String sa7[]=a[9].split(",");
String ba7=sa7[0];
String b7=ba7.replace("}","0");
String a9=sa7[1].substring(2,12);
String ba8=a[10].substring(3,14);
String b8=ba8.replace("dark_circle","黑眼圈:");
String sa8[]=a[11].split(",");
String b9=sa8[0];
String a10=sa8[1].substring(2,7);
String sa9[]=a[12].split(",");
String b10=sa9[0];
String a11=sa9[1].substring(2,6);
String sa10[]=a[13].split(",");
String b11=sa10[0];
String a12=sa10[1].substring(2,8);
String sa11[]=a[14].split(",");
String b12=sa11[0];
String a13=sa11[1].substring(2,8);
String b13=a[15].substring(3,15);
String sa12[]=a[16].split(",");
String b14=sa12[0];
String a14=sa12[1].substring(2,12);
String sa13[]=a[17].split(",");
String b15=sa13[0];
String a15=sa13[1].substring(2,11);
// String str2=getFaceToken(response1);
Log.i("kk", faceToken1.toString());
faces.add(faceToken1);
// sb.append("faceToken1: ");
// sb.append(faceToken1);
sb.append(a0.replace("attributes","面部特征:"));
sb.append("\n");
sb.append(a1.replace("emotion","心情检测:"));
sb.append("\n");
sb.append(a2.replace("sadness","悲伤:"));
sb.append(b1);
sb.append("\n");
sb.append(a3.replace("neutral","平静:"));
sb.append(b2);
sb.append("\n");
sb.append(a4.replace("disgust","厌恶:"));
sb.append(b3);
sb.append("\n");
sb.append(a5.replace("anger","生气:"));
sb.append(b4);
sb.append("\n");
sb.append(a6.replace("surprise","惊讶:"));
sb.append(b5);
sb.append("\n");
sb.append(a7.replace("fear","恐惧:"));
sb.append(b6);
sb.append("\n");
sb.append(a8.replace("happiness","开心:"));
sb.append(b7);
sb.append("\n");
sb.append(a9.replace("skinstatus","皮肤检测:"));
sb.append("\n");
sb.append(b8);
sb.append(b9);
sb.append("\n");
sb.append(a10.replace("stain","色斑:"));
sb.append(b10);
sb.append("\n");
sb.append(a11.replace("acne","青春痘:"));
sb.append(b11);
sb.append("\n");
sb.append(a12.replace("health","健康:"));
sb.append(b12.replace("}","0"));
sb.append("\n");
sb.append(a13.replace("beauty","颜值检测:"));
sb.append("\n");
sb.append(b13.replace("female_score","男性眼中的你:"));
sb.append(b14);
sb.append("\n");
sb.append(a14.replace("male_score","女性眼中的你:"));
sb.append(b15.replace("}","0"));
sb.append("\n");
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
Response response2 = commonOperate.detectUrl(imagesub, 0, null);
String faceToken2 = getFaceToken(response2);
faces.add(faceToken2);
sb.append("faceToken2: ");
sb.append(faceToken2);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString());
}
});
Response res = commonOperate.compare(faceToken1,null,null,base,faceToken2,imagesub,null,null);
String result = new String(res.getContent());
String k[]=result.split(":");
Log.i("sub", k[1]);
String p[]=k[1].split(",");
Log.i("sub1", p[0]);
final double samelike = Double.parseDouble(p[0]);
Log.e("result", result);
sb.append("\n");
sb.append("\n");
sb.append("search result: ");
sb.append(result);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(sb.toString()+samelike);
if (samelike>10.000){
Log.i("jjj","事件响应");
Intent intent =new Intent(MainActivity.this,Main2Activity.class);
MainActivity.this.startActivity(intent);
}
}
});
} catch (Exception e) {
}
}
}).start();
}
/**
* 将图片上传
* @param imgpath
*/
private void upload(final String imgpath){
final BmobFile icon = new BmobFile(new File(imgpath));
// if(TextUtils.isEmpty(key) || TextUtils.isEmpty(secret)){
// AlertDialog.Builder builder = new AlertDialog.Builder(this);
// builder.setMessage("please enter key and secret");
// builder.setTitle("");
// builder.show();
// }else {
//
// new Thread(new Runnable() {
// @Override
// public void run() {
//
// CommonOperate commonOperate = new CommonOperate(key, secret, false);
// FaceSetOperate FaceSet = new FaceSetOperate(key, secret, false);
// ArrayList<String> faces = new ArrayList<>();
//
// //拿到人脸的详细位置信息
//
//
// try {
// //检测第一个人脸,传的是本地图片文件
// //detect first face by local file
//
// String attrs = new String();
//
// attrs = "emotion,beauty,ethnicity,facequality,skinstatus";
//
//
// Response response1 = commonOperate.detectUrl(imgpath, 0, attrs);
// final String res1 = new String(response1.getContent());
// final String str1 = res1.substring(291, 300).trim();
//
// String str6=res1.substring(res1.indexOf("{",res1.indexOf("{")+1), res1.indexOf("}",res1.indexOf("facequality"))).trim();
// String a[]=str6.split(":");
// String a0=a[0].substring(2,12);
// String a1=a[1].substring(3,10);
// String a2=a[2].substring(3,10);
//
// String sa1[]=a[3].split(",");
// String b1=sa1[0];
// String a3=sa1[1].substring(2,9);
//
// String sa2[]=a[4].split(",");
// String b2=sa2[0];
// String a4=sa2[1].substring(2,9);
//
// String sa3[]=a[5].split(",");
// String b3=sa3[0];
// String a5=sa3[1].substring(2,7);
//
// String sa4[]=a[6].split(",");
// String b4=sa4[0];
// String a6=sa4[1].substring(2,10);
//
// String sa5[]=a[7].split(",");
// String b5=sa5[0];
// String a7=sa5[1].substring(2,6);
//
// String sa6[]=a[8].split(",");
// String b6=sa6[0];
// String a8=sa6[1].substring(2,11);
//
// String sa7[]=a[9].split(",");
// String ba7=sa7[0];
// String b7=ba7.replace("}","0");
// String a9=sa7[1].substring(2,12);
//
// String ba8=a[10].substring(3,14);
// String b8=ba8.replace("dark_circle","黑眼圈:");
//
// String sa8[]=a[11].split(",");
// String b9=sa8[0];
// String a10=sa8[1].substring(2,7);
//
// String sa9[]=a[12].split(",");
// String b10=sa9[0];
// String a11=sa9[1].substring(2,6);
//
// String sa10[]=a[13].split(",");
// String b11=sa10[0];
// String a12=sa10[1].substring(2,8);
//
// String sa11[]=a[14].split(",");
// String b12=sa11[0];
// String a13=sa11[1].substring(2,8);
//
// String b13=a[15].substring(3,15);
//
// String sa12[]=a[16].split(",");
// String b14=sa12[0];
// String a14=sa12[1].substring(2,12);
//
// String sa13[]=a[17].split(",");
// String b15=sa13[0];
// String a15=sa13[1].substring(2,11);
//
//
// String faceToken1 = getFaceToken(response1);
// // String str2=getFaceToken(response1);
// Log.i("kk", faceToken1.toString());
//
//
//
// faces.add(faceToken1);
//// sb.append("faceToken1: ");
//// sb.append(faceToken1);
// sb.append(a0.replace("attributes","面部特征:"));
// sb.append(a1.replace("emotion","心情:"));
// sb.append(a2.replace("sadness","悲伤:"));
// sb.append(b1);
// sb.append(a3.replace("neutral","平静:"));
// sb.append(b2);
// sb.append(a4.replace("disgust","厌恶:"));
// sb.append(b3);
// sb.append(a5.replace("anger","生气:"));
// sb.append(b4);
// sb.append(a6.replace("surprise","惊讶:"));
// sb.append(b5);
// sb.append(a7.replace("fear","恐惧:"));
// sb.append(b6);
// sb.append(a8.replace("happiness","开心:"));
// sb.append(b7);
// sb.append(a9.replace("skinstatus","面部特征:"));
// sb.append(b8);
// sb.append(b9);
// sb.append(a10.replace("stain","色斑:"));
// sb.append(b10);
// sb.append(a11.replace("acne","青春痘:"));
// sb.append(b11);
// sb.append(a12.replace("health","健康:"));
// sb.append(b12.replace("}","0"));
// sb.append(a13.replace("beauty","颜值:"));
// sb.append(b13.replace("female_score","男性眼中的你:"));
// sb.append(b14);
// sb.append(a14.replace("male_score","女性眼中的你:"));
// sb.append(b15.replace("}","0"));
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// mTextView.setText(sb.toString());
//
//
// }
// });
// Response res = commonOperate.searchByOuterId(null, imageUrl, null, "test", 1);
// String result = new String(res.getContent());
// Log.e("result", result);
// sb.append("\n");
// sb.append("\n");
// sb.append("search result: ");
// sb.append(result);
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// mTextView.setText(sb.toString());
// }
// });
//
// } catch (Exception e) {
//
//
// }
// }
// }).start();
// }
icon.uploadblock(new UploadFileListener() {
@Override
public void done(BmobException e) {
if(e==null){
Cloth cloth=new Cloth(icon);
cloth.save(new SaveListener<String>() {
@Override
public void done(String s, BmobException e) {
if(e==null){
Log.d("bmob", "成功");
}else{
Log.i("bmob","失败:"+e.getMessage()+","+e.getErrorCode());
}
}
});
}else{
//toast("上传文件失败:" + e.getMessage());
}
}
@Override
public void onProgress(Integer value) {
// 返回的上传进度(百分比)
}
});
}
}