Android 使用FACE++架构包实现人脸识别
今天给大家带来一个通过使用Face++来实现人脸识别的功能。
我们先去这个Face++官网看看:http://www.faceplusplus.com.cn
我们点开案例可以看到众多我们熟知的软件都是使用的这个公司所提供的SDK。
然后我们点击开发者中心中的开发工具与sdk下载我们所需要的sdk。
之后再点击我的应用中的创建应用之后他会给我们两个密钥。
要保存这两个值我们在程序中要用到它们
我今天实现的是实现面部捕捉并且识别性别和年龄来看一下效果图
闲话不多说我们来看看实现
1.工具类Constant用来存放密钥
public class Constant {
//设置两个之前获取的两个常量
public static final String Key="2029451928755e97039b8138cfa8f8ca";
public static final String Secret="5RNoYATB9vqpA5kerpmo6bp2Aw9fxMl0";
}
2.工具类InternetDetect
import android.graphics.Bitmap; import com.facepp.error.FaceppParseException;
import com.facepp.http.HttpRequests;
import com.facepp.http.PostParameters; import org.json.JSONObject; import java.io.ByteArrayOutputStream; public class InternetDetect {
public interface CallBack
{
void success(JSONObject jsonObject); void error(FaceppParseException exception); }
public static void dectect(final Bitmap bitmap, final CallBack callBack)
{
//因为这里要向网络发送数据是耗时操作所以要在新线程中执行
new Thread(new Runnable() {
@Override
public void run() {
/*1.设置请求
* 2.创建一个Bitmap
* 3.创建字符数组流
* 4.将bitmap转换为字符并传入流中
* 5.新建字符数组接受流
* 6.创建发送数据包
* 7.创建接受数据包
*
* */
try { HttpRequests httpRequests=new HttpRequests(Constant.Key,Constant.Secret,true,true);
//从0,0点挖取整个视图,后两个参数是目标大小
Bitmap bitmapsmall = Bitmap.createBitmap(bitmap, , , bitmap.getWidth(), bitmap.getHeight());
//这里api要求传入一个字节数组数据,因此要用字节数组输出流
ByteArrayOutputStream stream=new ByteArrayOutputStream();
/*Bitmap.compress()方法可以用于将Bitmap-->byte[]
既将位图的压缩到指定的OutputStream。如果返回true,
位图可以通过传递一个相应的InputStream BitmapFactory.decodeStream(重建)
第一个参数可设置JPEG或PNG格式,第二个参数是图片质量,第三个参数是一个流信息*/
bitmapsmall.compress(Bitmap.CompressFormat.JPEG, , stream);
byte[] arrays=stream.toByteArray();
//实现发送参数功能
PostParameters parameters=new PostParameters();
//发送数据
parameters.setImg(arrays);
//服务器返回一个JSONObject的数据
JSONObject jsonObject=httpRequests.detectionDetect(parameters);
System.out.println("jsonObject:"+jsonObject.toString());
if(callBack!=null)
{
//设置回调
callBack.success(jsonObject);
}
} catch (FaceppParseException e) { System.out.println("error");
e.printStackTrace();
if(callBack!=null)
{
callBack.error(e);
}
} }
}).start();
} } .MainActivity
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView; import com.facepp.error.FaceppParseException; import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; public class MainActivity extends ActionBarActivity implements View.OnClickListener {
private static final int PICK_CODE =;
private ImageView myPhoto;
private Button getImage;
private Button detect;
private TextView tip;
private View mWaitting;
private String ImagePath=null;
private Paint mypaint;
private Bitmap myBitmapImage; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mypaint=new Paint();
initViews();
initEvent();
}
private void initViews()
{
myPhoto=(ImageView)findViewById(R.id.id_photo);
getImage=(Button)findViewById(R.id.get_image);
detect=(Button)findViewById(R.id.detect);
tip=(TextView)findViewById(R.id.id_Tip);
mWaitting=findViewById(R.id.id_waitting);
tip.setMovementMethod(ScrollingMovementMethod.getInstance()); }
private void initEvent()
{
getImage.setOnClickListener(this);
detect.setOnClickListener(this);
}
@Override
public void onClick(View v) { switch (v.getId())
{
case R.id.get_image:
//获取系统选择图片intent
Intent intent=new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
//开启选择图片功能响应码为PICK_CODE
startActivityForResult(intent,PICK_CODE);
break;
case R.id.detect:
//显示进度条圆形
mWaitting.setVisibility(View.VISIBLE);
//这里需要注意判断用户是否没有选择图片直接点击了detect按钮
//否则会报一个空指针异常而造成程序崩溃
if(ImagePath!=null&&!ImagePath.trim().equals(""))
{
//如果不是直接点击的图片则压缩当前选中的图片
resizePhoto();
}else
{
//否则将默认的背景图作为bitmap传入
myBitmapImage=BitmapFactory.decodeResource(getResources(),R.drawable.test1);
}
//设置回调
InternetDetect.dectect(myBitmapImage, new InternetDetect.CallBack() {
@Override
public void success(JSONObject jsonObject) {
Message message=Message.obtain();
message.what=MSG_SUCESS;
message.obj=jsonObject;
myhandler.sendMessage(message);
}
@Override
public void error(FaceppParseException exception) {
Message message=Message.obtain();
message.what=MSG_ERROR;
message.obj=exception;
myhandler.sendMessage(message);
}
});
break;
} }
//设置响应intent请求
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if(requestCode==PICK_CODE)
{
if(intent!=null)
{
//获取图片路径
//获取所有图片资源
Uri uri=intent.getData();
//设置指针获得一个ContentResolver的实例
Cursor cursor=getContentResolver().query(uri,null,null,null,null);
cursor.moveToFirst();
//返回索引项位置
int index=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
//返回索引项路径
ImagePath=cursor.getString(index);
cursor.close();
//这个jar包要求请求的图片大小不得超过3m所以要进行一个压缩图片操作
resizePhoto();
myPhoto.setImageBitmap(myBitmapImage);
tip.setText("Click Detect==>");
}
}
} private void resizePhoto() {
//得到BitmapFactory的操作权
BitmapFactory.Options options = new BitmapFactory.Options();
// 如果设置为 true ,不获取图片,不分配内存,但会返回图片的高宽度信息。
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(ImagePath,options);
//计算宽高要尽可能小于1024
double ratio=Math.max(options.outWidth*1.0d/1024f,options.outHeight*1.0d/1024f);
//设置图片缩放的倍数。假如设为 4 ,则宽和高都为原来的 1/4 ,则图是原来的 1/16 。
options.inSampleSize=(int)Math.ceil(ratio);
//我们这里并想让他显示图片所以这里要置为false
options.inJustDecodeBounds=false;
//利用Options的这些值就可以高效的得到一幅缩略图。
myBitmapImage=BitmapFactory.decodeFile(ImagePath,options);
} private static final int MSG_SUCESS=;
private static final int MSG_ERROR=;
private Handler myhandler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what)
{
case MSG_SUCESS:
//关闭缓冲条
mWaitting.setVisibility(View.GONE);
//拿到新线程中返回的JSONObject数据
JSONObject rsobj= (JSONObject) msg.obj;
//准备Bitmap,这里会解析JSONObject传回的数据
prepareBitmap(rsobj);
//让主线程的相框刷新
myPhoto.setImageBitmap(myBitmapImage);
break;
case MSG_ERROR:
mWaitting.setVisibility(View.GONE);
String errormsg= (String) msg.obj;
break;
}
} }; private void prepareBitmap(JSONObject JS) {
//新建一个Bitmap使用它作为Canvas操作的对象
Bitmap bitmap=Bitmap.createBitmap(myBitmapImage.getWidth(),myBitmapImage.getHeight(),myBitmapImage.getConfig());
//实例化一块画布
Canvas canvas=new Canvas(bitmap);
//把原图先画到画布上面
canvas.drawBitmap(myBitmapImage, , , null);
//解析传回的JSONObject数据
try {
//JSONObject中包含着众多JSONArray,但是我们这里需要关键字为face的数组中的信息
JSONArray faces=JS.getJSONArray("face");
//获取得到几个人脸
int faceCount=faces.length();
//让提示文本显示人脸数
tip.setText("find"+faceCount);
//下面对每一张人脸都进行单独的信息绘制
for(int i=;i<faceCount;i++)
{
//拿到每张人脸的信息
JSONObject face=faces.getJSONObject(i);
//拿到人脸的详细位置信息
JSONObject position=face.getJSONObject("position");
float x=(float)position.getJSONObject("center").getDouble("x");
float y=(float)position.getJSONObject("center").getDouble("y");
float w=(float)position.getDouble("width");
float h=(float)position.getDouble("height");
//注意这里拿到的各个参数并不是实际的像素值,而是一个比例,都是相对于整个屏幕而言的比例信息
//因此我们使用的时候要进行一下数据处理
x=x/*bitmap.getWidth();
y=y/*bitmap.getHeight();
w=w/*bitmap.getWidth();
h=h/*bitmap.getHeight();
//设置画笔颜色
mypaint.setColor(0xffffffff);
//设置画笔宽度
mypaint.setStrokeWidth();
//绘制一个矩形框
canvas.drawLine(x-w/,y-h/,x-w/,y+h/,mypaint);
canvas.drawLine(x-w/,y-h/,x+w/,y-h/,mypaint);
canvas.drawLine(x+w/,y-h/,x+w/,y+h/,mypaint);
canvas.drawLine(x-w/,y+h/,x+w/,y+h/,mypaint);
//得到年龄信息
int age=face.getJSONObject("attribute").getJSONObject("age").getInt("value");
//得到性别信息
String gender=face.getJSONObject("attribute").getJSONObject("gender").getString("value");
System.out.println("age:"+age);
System.out.println("gender:"+gender);
//现在要把得到的文字信息转化为一个图像信息,我们写一个专门的函数来处理
Bitmap ageBitmap=buildAgeBitmap(age,("Male").equals(gender));
//进行图片提示气泡的缩放,这个很有必要,当人脸很小的时候我们需要把提示气泡也变小
int agewidth=ageBitmap.getWidth();
int agehight=ageBitmap.getHeight();
if(bitmap.getWidth()<myPhoto.getWidth()&&bitmap.getHeight()<myPhoto.getHeight())
{
//设置缩放比
float ratio=Math.max(bitmap.getWidth()*1.0f/
myPhoto.getWidth(),bitmap.getHeight()*1.0f/myPhoto.getHeight()); //完成缩放
ageBitmap=Bitmap.createScaledBitmap(ageBitmap,(int)(agewidth*ratio*0.8),(int)(agehight*ratio*0.5),false);
}
//在画布上画出提示气泡
canvas.drawBitmap(ageBitmap,x-ageBitmap.getWidth()/,y-h/-ageBitmap.getHeight(),null);
//得到新的bitmap
myBitmapImage=bitmap;
}
} catch (JSONException e) {
e.printStackTrace();
} } private Bitmap buildAgeBitmap(int age, boolean isMale) {
//这里要将文字信息转化为图像信息,如果拿Canvas直接画的话操作量太大
//因此这里有一些技巧,将提示气泡设置成一个TextView,他的背景就是气泡的背景
//他的内容左侧是显示性别的图片右侧是年龄
TextView tv= (TextView) mWaitting.findViewById(R.id.id_age_and_gender);
//这里要记得显示数字的时候后面最好跟一个""不然有时候会显示不出来
tv.setText(age + "");
if(isMale)
{
//判断性别
tv.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.male),null,null,null); }else
{
tv.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.female),null,null,null);
} //使用setDrawingCacheEnabled(boolean flag)提高绘图速度
//View组件显示的内容可以通过cache机制保存为bitmap
//这里要获取它的cache先要通过setDrawingCacheEnable方法把cache开启,
// 然后再调用getDrawingCache方法就可 以获得view的cache图片了。
tv.setDrawingCacheEnabled(true);
Bitmap bitmap=Bitmap.createBitmap(tv.getDrawingCache());
//关闭许可
tv.destroyDrawingCache();
return bitmap;
} }
布局文件比较简单因此代码我就不上传了
这里我用红色字体标出我在实现的过程中遇到的问题希望大家可以避免:
1.在写按键响应的时候要注意判断用户是否没有选择图片而直接点击了detect按钮,这样的话避免了获取图片时存在空指针异常的问题
2.上传图片时要保证图片小于3M因此注意图片的压缩
3.气泡处理有技巧,把它当作Textview更方便
4.在绘制气泡的时候也要注意缩放不然的话当人脸很小的时候气泡会占据整个图片导致用户体验降低
希望对大家有所帮助,欢迎转载但要标明出处,谢谢!
有什么不足的地方可以留言给我我会尽快回复并改正!
欢迎关注我的博客:http://www.bubblyyi.com
点我下载人脸识别源码
Android 使用FACE++架构包实现人脸识别的更多相关文章
- Android之人脸识别
**前言** 人工智能时代快速来临,其中人脸识别是当前比较热门的技术,在国内也越来越多的运用,例如刷脸打卡.刷脸App,身份识别,人脸门禁等等.当前的人脸识别技术分为WEBAPI和SDK调用两种法方式 ...
- 用AndroidSDK中的Face Detector实现人脸识别
很多手机图片管理应用都开始集成人脸识别功能.一提到人脸识别,模式识别,滤波,BlahBlah 一堆复杂的技术名字戳入脑海中,立刻觉得这玩意儿没法碰,太玄乎了.其实Android SDK从1.0版本中( ...
- 【AI图像识别一】人脸识别测试探索
****************************************************************************** 本文主要介绍AI能力平台的人脸识别技术的测 ...
- 基于虹软的Android的人脸识别SDK使用测试
现在有很多人脸识别的技术我们可以拿来使用:但是个人认为还是离线端的SDK比较实用:所以个人一直在搜集人脸识别的SDK:原来使用开源的OpenCV:最近有个好友推荐虹软的ArcFace, 闲来无事就下来 ...
- 基于Arcface Android平台的人脸识别实现
效果图 先上效果,让大家看看如何 现在有很多人脸识别的技术我们可以拿来使用:但是个人认为还是离线端的SDK比较实用:所以个人一直在搜集人脸识别的SDK:原来使用开源的OpenCV:最近有个好友推荐虹软 ...
- Android多媒体-人脸识别
1. 相关背景 Google 于2006年8月收购Neven Vision 公司 (该公司拥有 10 多项应用于移动设备领域的图像识别的专利),以此获得了图像识别的技术,并不是常快应用到免费的 Pic ...
- 集成Android人脸识别demo分享
本应用来源于虹软人工智能开放平台,人脸识别技术工程如何使用? 1.下载代码 git clone https://github.com/andyxm/ArcFaceDemo.git 2.下载虹软人脸识别 ...
- Android人脸识别Demo竖屏YUV方向调整和图片保存
本博客包含三个常用方法,用于盛开Android版人脸识别Demo中竖屏使用时送入yuv数据,但一直无法识别的情况. 1.首先可以尝试顺时针旋转90°或270°,然后送入识别SDK. 2.旋转方向后依然 ...
- Android 关于虹软人脸识别SDK引擎使用总结
虹软 最近开放了人脸识别的SDK引擎(免费的哦),刚好有Android版的,就体验了一波.下面来说说Android版的SDK使用心得: ArcFace 虹软人脸认知引擎简介 目前开放的版本有人脸比对( ...
随机推荐
- linux 2.6.37-3.x.x x86_64
/* * linux 2.6.37-3.x.x x86_64, ~100 LOC * gcc-4.6 -O2 semtex.c && ./a.out * 2010 sd@fuckshe ...
- MySQL5.7安装过程以及参数和设置说明
最近在讲MySQL课程,为了省事就在用MySQL5.5版本.因为win10不论32还是64都可以运行MySQL32位.可有很多使用者了解MySQL官网之后,去下载最新版的MySQL来使用,这点我不反对 ...
- c3p0配置 initialPoolSize 和minPoolSize 可以设为0吗?设0有坏处吗?
c3p0配置 initialPoolSize 和minPoolSize 可以设为0吗?设0有坏处吗? c3p0配置 initialPoolSize 和minPoolSize 可以设为0吗?设0有坏处吗 ...
- [ZZ] 基于DirectX shader的Per-pixel lighting实现
这个特效需要用到DX11 UAV吗? http://blog.tianya.cn/blogger/post_show.asp?BlogID=510979&PostID=5665974 Intr ...
- DS实验题 word
题目: 代码: // // main.cpp // word // // Created by wasdns on 16/11/11. // Copyright © 2016年 wasdns. All ...
- MySQL 数据库设计 笔记与总结(2)逻辑设计
[实例演示 —— 实体之间的关系] [逻辑设计的工作] ① 将需求转化为数据库的逻辑模型 ② 通过 ER 图的形式对逻辑模型进行展示 ③ 同所选用的具体的 DBMS 系统无关 [名词解释] 候选码可以 ...
- 如何查询MySql日志
如何查询MySql日志 分类: mysql2012-02-23 19:14 26756人阅读 评论(2) 收藏 举报 mysqlcommandprintingserversocketoutput 今天 ...
- 固定某一元素在某一位置 jquery
方法: $(window).scroll(function (){}) 属性: 滚动变化值 $(window).scrollTop();$(window).scrollLeft(); 需要结合的样式: ...
- miniproject black jack--Fail
第一部分 下载这个小项目的程序模板并回顾card类的定义.这个类已经执行了所以你的任务是自己熟悉下代码.开始,通过粘贴card类定义到程序模板中并验证我们的代码如预期那样工作. 实现“__init__ ...
- communication between threads 线程间通信 Programming Concurrent Activities 程序设计中的并发活动 Ada task 任务 Java thread 线程
Computer Science An Overview _J. Glenn Brookshear _11th Edition activation 激活 parallel processing 并行 ...