转载

How Old Are You?

今天要分享的是一个测年龄的小应用,就类似是 http://how-old.net 官网测年龄的功能一样,我的也是这样一个功能,细节捕获当然没有how-old多啦,不过这些主要是基于一个第三方的jar包,我这里用到的是Face++的jar包,用到的是这个版本: Java SDK (Android) (Android2.3及以上)。

一、功能展示:

How Old Are You?

How Old Are You?

How Old Are You?

图一展示的是从图库选择测试图片的一个界面;图二是解析欲测试图片的一个界面;图三是一个测试结果的界面。下面说一下这个小应用的一些要点:

  • 实现图库:

这里是采用Intent.ACTION_PICK中调用图库的用法来实现的,当然图片也要适当的压缩;

public void onClick(View v) {   switch (v.getId()) {   case R.id.btnGetImage:    Intent intent=new Intent(Intent.ACTION_PICK);    intent.setType("image/*");    startActivityForResult(intent,PICK_CODE);    break;   }  } onClick() 
@Override  protected void onActivityResult(int requestCode, int resultCode, Intent intent) {   if (requestCode==PICK_CODE)    {    if (intent!=null)     {     Uri uri=intent.getData();     Cursor cursor=getContentResolver().query(uri, null, null,null, null);     cursor.moveToFirst();     int idx=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);     currentPhotoString=cursor.getString(idx);     cursor.close();     resizePhono();     ivPhoto.setImageBitmap(photoImg);     tvTip.setText("Click Detect==>");    }   }   super.onActivityResult(requestCode, resultCode, intent);  }  /**   * 压缩图片   */  private void resizePhono() {   BitmapFactory.Options options=new BitmapFactory.Options();   options.inJustDecodeBounds=true;//仅仅加载图片   BitmapFactory.decodeFile(currentPhotoString, options);   double radio=Math.max(options.outWidth*1.0d/1024f, options.outHeight*1.0d/1024f);   options.inSampleSize=(int) Math.ceil(radio);   options.inJustDecodeBounds=false;   photoImg=BitmapFactory.decodeFile(currentPhotoString,options);  } onActivityResult 
  • detect工具类实现:

对应于detect()方法,如果图片解析成功的话,就返回一个json字符串;如果失败的话,返回一个异常的数据。对应于不同的情况有不同的返回值,我定义了一个 CallBackinterface ;detetct()方法内部呢是要执行一个耗时的操作,所以要开启一个线程;那这个方法的主要功能也就是 将图片转化为字节数组,封装到PostParameters中,通过detectionDetect方法返回JSON对象。

package com.example.how_old; import java.io.ByteArrayOutputStream; import org.json.JSONObject; import android.graphics.Bitmap; import android.util.Log; import com.facepp.error.FaceppParseException; import com.facepp.http.HttpRequests; import com.facepp.http.PostParameters; public class FaceppDetect {  public interface CallBack  {   void success(JSONObject result);   void error(FaceppParseException exception);  }  /**   * 将图片转化为字节数组,封装到PostParameters中,   * 通过detectionDetect方法返回JSON对象   * @param bitmap   * @param callBack   */  public static void detect(final Bitmap bitmap,final CallBack callBack)  {   //匿名内部类参数最好声明成final类型   Log.v("aaaaa", "aaaa");   new Thread(new Runnable() {    public void run() {     try {      Log.v("qqq", "11111");      HttpRequests requests=new HttpRequests(Constant.KET,Constant.SECRET,true,true);      Bitmap bmSmall=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight());      ByteArrayOutputStream stream=new ByteArrayOutputStream();      bmSmall.compress(Bitmap.CompressFormat.JPEG, 100, stream);      byte[] arrays=stream.toByteArray();      PostParameters parameters=new PostParameters();      parameters.setImg(arrays);      JSONObject jsonObject=requests.detectionDetect(parameters);      Log.v("TAG", jsonObject.toString());      if (callBack!=null) {       callBack.success(jsonObject);      }     } catch (FaceppParseException e) {      e.printStackTrace();      if (callBack!=null) {       callBack.error(e);      }     }    }   }).start();  } } detect 
  • JSON解析,捕获属性值:

根据JSON的格式我们来解析JSON,获取到我们想要的属性值,说到JSON解析呢,有一个很好的JSON校验网站,bejson.com可以将无序的json字符串格式化,能帮助我们很好的解析json。将获取到的属性值赋值给控件,这里需要在主线程中更新UI,当然这也是Android的一个机制, Handler机制 ,通过Handler来更新主线程中的UI。

JSONArray faces;     faces = rs.getJSONArray("face");     int faceCount=faces.length();     tvTip.setText("find "+faceCount);     for (int i = 0; i < faceCount; i++) {      JSONObject face = faces.getJSONObject(i);      JSONObject posObj=face.getJSONObject("position");      float x=(float) posObj.getJSONObject("center").getDouble("x");      float y=(float) posObj.getJSONObject("center").getDouble("y");      float w=(float) posObj.getDouble("width");      float h=(float) posObj.getDouble("height");      x=x/100*bitmap.getWidth();      y=y/100*bitmap.getHeight();      w=w/100*bitmap.getWidth();      h=h/100*bitmap.getHeight(); 解析JSON 
private Handler handler=new Handler()  {   public void handleMessage(android.os.Message msg)    {    switch (msg.what) {    case MSG_SUCCESS:     flWaitting.setVisibility(View.GONE);     JSONObject rs=(JSONObject) msg.obj;     prepareRsBitmap(rs);     ivPhoto.setImageBitmap(photoImg);     break;    case MSG_ERROR:     flWaitting.setVisibility(View.GONE);     String errorMsg=(String) msg.obj;     if (TextUtils.isEmpty(errorMsg)) {      tvTip.setText("Error");     }     else {      tvTip.setText(errorMsg);     }     break;    }    super.handleMessage(msg);   } Handler 
  • 绘制脸部及年龄显示框:

脸部显示框用到的主要的一个方法就是Canvas的drawLine()方法,而显示年龄的气泡,我们采用一个简单的控件 Textview 控件,简单设置下背景background、drawableLeft、text即可显示出气泡的赶脚,无需使用Canvas绘制。而主要的一个就是如何将Textview转化为Bitmap,我写了一个 buildAgeBitmap() 方法来转化。当然对应于图片的缩放,我们的气泡也是有一个缩放的,这样才合理嘛。哇咔咔!!!!

mPaint.setColor(0xffffffff);  mPaint.setStrokeWidth(3);  //画脸部方框  canvas.drawLine(x-w/2, y-h/2, x-w/2, y+h/2,mPaint);  canvas.drawLine(x-w/2, y-h/2, x+w/2, y-h/2,mPaint);  canvas.drawLine(x+w/2, y-h/2, x+w/2, y+h/2,mPaint);  canvas.drawLine(x-w/2, y+h/2, x+w/2, y+h/2,mPaint);  JSONObject attrObj=face.getJSONObject("attribute");  int ageValue=attrObj.getJSONObject("age").getInt("value"); // int ageRange=attrObj.getJSONObject("age").getInt("range");  String genderValue=attrObj.getJSONObject("gender").getString("value");  Bitmap ageBitmap=buildAgeBitmap(ageValue,"Male".equals(genderValue));  //气泡缩放  int ageWidth=ageBitmap.getWidth();  int ageHeight=ageBitmap.getHeight();  if (bitmap.getWidth()<ivPhoto.getWidth()&&bitmap.getHeight()<ivPhoto.getHeight()) {      float ratio=Math.max(bitmap.getWidth()*1.0f/ivPhoto.getWidth(), bitmap.getHeight()*1.0f/ivPhoto.getHeight());      ageBitmap=Bitmap.createScaledBitmap(ageBitmap, (int)(ageWidth*ratio),(int)(ageHeight*ratio), false);  }  canvas.drawBitmap(ageBitmap, x-ageBitmap.getWidth()/2,y-h/2-ageBitmap.getHeight(), null);  photoImg=bitmap; 绘制显示框 
private Bitmap buildAgeBitmap(int ageValue, boolean isMale) {    TextView tvQp=(TextView) flWaitting.findViewById(R.id.tvAgeGender);    tvQp.setText(ageValue+"");    if (isMale) {     tvQp.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.male), null, null, null);    }    else {     tvQp.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.female), null, null, null);    }    tvQp.setDrawingCacheEnabled(true);    Bitmap bitmap=Bitmap.createBitmap(tvQp.getDrawingCache());    tvQp.destroyDrawingCache();    return bitmap;   }; buildAgeBitmap 

其中,JSON解析和绘制显示框等操作都是放置在prepareRsBitmap()方法中的。基本上就这么多啦。啊你哦!!!!o 0我应该大家看一下JSON的格式:

{  "face": [   {    "position": {     "mouth_right": {      "y": 28.476451,      "x": 54.946591     },     "mouth_left": {      "y": 30.445734,      "x": 44.776818     },     "center": {      "y": 25.255973,      "x": 47.272727     },     "height": 16.723549,     "width": 22.272727,     "nose": {      "y": 25.643515,      "x": 45.270455     },     "eye_left": {      "y": 23.175427,      "x": 41.6     },     "eye_right": {      "y": 21.064334,      "x": 51.402045     }    },    "attribute": {     "race": {      "value": "Asian",      "confidence": 99.53840000000001     },     "gender": {      "value": "Female",      "confidence": 99.9903     },     "smiling": {      "value": 88.3311     },     "age": {      "value": 35,      "range": 7     }    },    "tag": "",    "face_id": "ac2a5139c9293e8b18d2cc26fe6b3d54"   }  ],  "session_id": "09739b73148e45af98a1dd87671973d4",  "img_height": 586,  "img_width": 440,  "img_id": "ff62269ed6c0fff160d8f3a77c2eb4e3",  "url": null,  "response_code": 200 } JSON 
正文到此结束
Loading...