今天要分享的是一個測年齡的小應用,就類似是http://how-old.net官網測年齡的功能一樣,我的也是這樣一個功能,細節捕獲當然沒有how-old多啦,不過這些主要是基於一個第三方的jar包,我這里用到的是Face++的jar包,用到的是這個版本:Java SDK (Android) (Android2.3及以上)。
一、功能展示:
圖一展示的是從圖庫選擇測試圖片的一個界面;圖二是解析欲測試圖片的一個界面;圖三是一個測試結果的界面。下面說一下這個小應用的一些要點:
- 實現圖庫:
這里是采用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; } }

@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); }
- detect工具類實現:
對應於detect()方法,如果圖片解析成功的話,就返回一個json字符串;如果失敗的話,返回一個異常的數據。對應於不同的情況有不同的返回值,我定義了一個CallBack的interface;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(); } }
- 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();

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); }
- 繪制臉部及年齡顯示框:
臉部顯示框用到的主要的一個方法就是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; };
其中,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 }