Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android人臉識別——HowOld測測你的年齡和性別

android人臉識別——HowOld測測你的年齡和性別

編輯:關於Android編程

引言

這段時間微軟的HowOldRobot 測試年齡的網站非常火,訪問量已經爆棚了!不過,這個測試也有很多比較坑爹的地方。比如:。。。。。
這裡寫圖片描述

再比如。。。

這裡寫圖片描述

好了 言歸正傳!今天我們就來看看android中怎麼利用人臉識別功能來實現我們自己的HowOld APP
(PS:本人也是借鑒了網上大神的視頻和資料 然後自己加以改進)

Face++ API

想要使用人臉識別功能,我們需要調用Face++中的一些API來完成工作。Face++的官網地址是:http://www.faceplusplus.com.cn/
使用Face++有幾個步驟:
1.注冊賬號
這裡寫圖片描述
2.創建應用
這裡寫圖片描述
3.復制 Key 和 Sercret
這裡寫圖片描述
4.下載SDK
這裡寫圖片描述
5.將SDK放入我們的工程lib目錄中<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrrDwcsg17yxuLmk1/fX9s3qwcujrL3Tz8LAtL7Nv6rKvLHg0LTO0sPHtcSzzNDywcuhozwvcD4NCjxoMiBpZD0="布局">布局

首先是布局文件。下面是我們的界面視圖。很簡單的布局,上邊一張圖片 ,下邊一個TextView加三個按鈕 。沒有太多好說的。
這裡寫圖片描述

MainActivity.xml



    

    
    
       
        

        

    

人臉識別工具類

接下來, 我們需要編寫一個人臉識別工具類,根據我們傳入的圖片來進行識別並返回數據。其中Constact是我們的常量工具類,存放我們應用的Key和Secret.

FaceRecognize.class

public class FaceRecognize
{
    // 回調接口
    public interface CallBack
    {
        // 識別成功
        void success(JSONObject result);

        // 識別失敗
        void error(FaceppParseException e);
    }

    // 開始識別
    public static void detect(final Bitmap bitmap, final CallBack callback)
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                HttpRequests request = new HttpRequests(Constant.KEY,
                        Constant.SECRET, true, true);
                Bitmap bmSmall = Bitmap.createBitmap(bitmap, 0, 0,
                        bitmap.getWidth(), bitmap.getHeight());
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                //壓縮圖片
                bmSmall.compress(Bitmap.CompressFormat.JPEG, 100, baos);
                byte[] datas = baos.toByteArray();
                PostParameters params = new PostParameters();
                params.setImg(datas);
                try
                {
                    //如果識別成功,調用success回調函數
                    JSONObject result = request.detectionDetect(params);
                    if (callback != null)
                    {
                        callback.success(result);
                    }
                } catch (FaceppParseException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    if (callback != null)
                    {
                        callback.error(e);
                        Log.e(JSONObject, e.toString());
                    }
                }
            }
        }).start();
    }
}

程序主要邏輯

然後我們在MainActivity中實現我們的主要邏輯。

MainActivity.class

public class MainActivity extends Activity implements OnClickListener
{
    //從相冊選擇照片
    private static final int PICK_CODE = 0X110;
    //照相
    private static final int TAKE_PICTURE = 0X114;
    //識別成功
    private static final int MSG_SUCCESS = 0X111;
    //識別失敗
    private static final int MSG_ERROR = 0X112;
    //剪裁圖片
    private static final int CROP_PHOTO = 0x115;


    private ImageButton detect, camera, photo;
    private TextView tip, ageAndgender;
    private ImageView img;
    private String mCurrentPhotoPath;
    private Bitmap mPhotoImg;
    private Canvas mCanvas;
    private Paint mPaint;

    //自定義對話框
    private CustomProgressDialog dialog;

    private Uri imageUri;
    private String filename;

    private boolean isCamera =false;

    private Dialog dialogs;



    private Handler mHandler = new Handler()
    {
        public void handleMessage(android.os.Message msg)
        {
            switch (msg.what)
            {
                //解析成功
                case MSG_SUCCESS:
                    dialog.dismiss();
                    //獲取JSON數據
                    JSONObject result = (JSONObject) msg.obj;
                    //解析JSON數據
                    parseResult(result);
                    img.setImageBitmap(mPhotoImg);
                    break;
                    //解析失敗
                case MSG_ERROR:
                    dialog.dismiss();
                    String errorMsg = (String) msg.obj;
                    if (TextUtils.isEmpty(errorMsg))
                    {
                        tip.setText(Error!!);
                    }
                    break;

                default:
                    break;
            }
        };
    };

在上面的代碼片段中,我們定義了一些常量和控件,並且使用handler來處理 識別成功和識別失敗兩種情況。

/**
     * 解析JSON數據
     * @param object
     */
    private void parseResult(JSONObject object)
    {
        Bitmap bitmap = Bitmap.createBitmap(mPhotoImg.getWidth(),
                mPhotoImg.getHeight(), mPhotoImg.getConfig());
        mCanvas = new Canvas(bitmap);
        mCanvas.drawBitmap(mPhotoImg, 0, 0, null);
        mPaint.setColor(Color.RED);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(3);
        mPaint.setStrokeCap(Cap.ROUND);
        JSONArray faces;
        try
        {
            faces = object.getJSONArray(face);
            int faceCount = faces.length();
            //未識別出人臉
            if (faceCount == 0)
            {
                dialogs = new AlertDialog.Builder(this)
                        .setTitle(檢測結果)
                        .setMessage(長得太抽象o(╯□╰)o,識別不出來)
                        .setNegativeButton(重來,
                                new DialogInterface.OnClickListener()
                                {

                                    @Override
                                    public void onClick(DialogInterface dialog,
                                            int which)
                                    {
                                        dialogs.dismiss();
                                    }
                                }).create();
                dialogs.show();
                return;
            }
            tip.setText(發現  + faceCount + 只 臉 );
            //循環處理每一張臉
            for (int i = 0; i < faceCount; i++)
            {
                JSONObject face = faces.getJSONObject(i);
                JSONObject position = face.getJSONObject(position);
                //臉部中心點X坐標
                float x = (float) position.getJSONObject(center).getDouble(
                        x);
                //臉部中心點Y坐標
                float y = (float) position.getJSONObject(center).getDouble(
                        y);
                //臉部寬度
                float width = (float) position.getDouble(width);
                //臉部高度
                float height = (float) position.getDouble(height);

                x = x / 100 * bitmap.getWidth();
                y = y / 100 * bitmap.getHeight();
                width = width / 100 * bitmap.getWidth();
                height = height / 100 * bitmap.getHeight();
                //繪制年齡性別的顯示框
                mCanvas.drawLine(x - width / 2, y - height / 2, x - width / 2,
                        y + height / 2, mPaint);
                mCanvas.drawLine(x - width / 2, y - height / 2, x + width / 2,
                        y - height / 2, mPaint);
                mCanvas.drawLine(x + width / 2, y - height / 2, x + width / 2,
                        y + height / 2, mPaint);
                mCanvas.drawLine(x - width / 2, y + height / 2, x + width / 2,
                        y + height / 2, mPaint);

                int age = face.getJSONObject(attribute).getJSONObject(age)
                        .getInt(value);
                String gender = face.getJSONObject(attribute)
                        .getJSONObject(gender).getString(value);

                Bitmap ageBitmap = buildAgeBitmap(age, gender.equals(Male));
                int ageWidth = ageBitmap.getWidth();
                int ageHeight = ageBitmap.getHeight();
                //對年齡性別的顯示框大小進行調整
                if (bitmap.getWidth() < img.getWidth()
                        && bitmap.getHeight() < img.getHeight())
                {

                    float ratio = Math.max(
                            bitmap.getWidth() * 1.0f / img.getWidth(),
                            bitmap.getHeight() * 1.0f / img.getHeight());

                    ageBitmap = Bitmap.createScaledBitmap(ageBitmap,
                            (int) (ageWidth * ratio),
                            (int) (ageHeight * ratio), false);
                }
                mCanvas.drawBitmap(ageBitmap, x - ageBitmap.getWidth() / 2, y
                        - height / 2 - ageBitmap.getHeight(), null);
                mPhotoImg = bitmap;
            }
        } catch (JSONException e)
        {
            e.printStackTrace();
        }

    }

在parseRusult方法中, 我們解析從服務器中返回的JSON數據,然後獲取到我們想要的年齡和性別,臉部位置等數據。
用於服務器返回的臉部中心坐標和寬高等數據是使用在圖片中的百分比所表示的,所以我們需要做下面的處理將之轉換成真實像素位置。

    //臉部中心點X坐標
                float x = (float) position.getJSONObject(center).getDouble(
                        x);
                //臉部中心點Y坐標
                float y = (float) position.getJSONObject(center).getDouble(
                        y);
                //臉部寬度
                float width = (float) position.getDouble(width);
                //臉部高度
                float height = (float) position.getDouble(height);

                x = x / 100 * bitmap.getWidth();
                y = y / 100 * bitmap.getHeight();
                width = width / 100 * bitmap.getWidth();
                height = height / 100 * bitmap.getHeight();

然後 ,我們繪制臉部的識別框,就是示例圖中的那個紅色方框。他們是四條線段繪制的。

    //繪制年齡性別的顯示框
                mCanvas.drawLine(x - width / 2, y - height / 2, x - width / 2,
                        y + height / 2, mPaint);
                mCanvas.drawLine(x - width / 2, y - height / 2, x + width / 2,
                        y - height / 2, mPaint);
                mCanvas.drawLine(x + width / 2, y - height / 2, x + width / 2,
                        y + height / 2, mPaint);
                mCanvas.drawLine(x - width / 2, y + height / 2, x + width / 2,
                        y + height / 2, mPaint);

下一步,我們還需要將 表示性別和年齡的顯示框繪制在相應的人臉框的上邊,並對顯示框做相應的校正,防止其過大。

    int age = face.getJSONObject(attribute).getJSONObject(age)
                        .getInt(value);
    String gender = face.getJSONObject(attribute)
                        .getJSONObject(gender).getString(value);
                //年齡顯示框的圖像
    Bitmap ageBitmap = buildAgeBitmap(age, gender.equals(Male));
                int ageWidth = ageBitmap.getWidth();
                int ageHeight = ageBitmap.getHeight();
                //對年齡性別的顯示框大小進行調整
                if (bitmap.getWidth() < img.getWidth()
                        && bitmap.getHeight() < img.getHeight())
                {

                    float ratio = Math.max(
                            bitmap.getWidth() * 1.0f / img.getWidth(),
                            bitmap.getHeight() * 1.0f / img.getHeight());

                    ageBitmap = Bitmap.createScaledBitmap(ageBitmap,
                            (int) (ageWidth * ratio),
                            (int) (ageHeight * ratio), false);
                }
                mCanvas.drawBitmap(ageBitmap, x - ageBitmap.getWidth() / 2, y
                        - height / 2 - ageBitmap.getHeight(), null);
                mPhotoImg = bitmap;
            }
        } catch (JSONException e)
        {
            e.printStackTrace();
        }

    }

我們的性別年齡顯示框其實就是一個TextView ,並在左邊通過drawableLeft設置了表示性別的圖片。buildAgeBitmap函數用於將TextView轉換為Bitmap對象

        //繪制年齡性別顯示框,將TextView轉換為Bitmap對象
    private Bitmap buildAgeBitmap(int age, boolean isMale)
    {
        ageAndgender = (TextView) getLayoutInflater().inflate(
                R.layout.age_layout, null);
        ageAndgender.setText(age + );
        if (isMale)
        {
            ageAndgender.setCompoundDrawablesWithIntrinsicBounds(getResources()
                    .getDrawable(R.drawable.male), null, null, null);
        } else
        {
            ageAndgender.setCompoundDrawablesWithIntrinsicBounds(getResources()
                    .getDrawable(R.drawable.female), null, null, null);
        }
        ageAndgender.setDrawingCacheEnabled(true);
        ageAndgender.measure(
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        ageAndgender.layout(0, 0, ageAndgender.getMeasuredWidth(),
                ageAndgender.getMeasuredHeight());
        ageAndgender.buildDrawingCache();
        Bitmap bitmap = Bitmap.createBitmap(ageAndgender.getDrawingCache());
        return bitmap;
    }

其中age_layout.xml就是性別年齡顯示框的布局文件。

age_layout.xml



   

拍照、相冊、識別處理

最後,我們需要對底部的拍照、相冊選擇圖片和識別按鈕進行處理。

@Override
    public void onClick(View v)
    {
        switch (v.getId())
        {
            //打開相冊 選取圖片
            case R.id.open_photo:
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_PICK);
                intent.setType(image/*);
                startActivityForResult(intent, PICK_CODE);
                break;
            //照相並獲取圖片
            case R.id.open_camera:
                //圖片名稱 時間命名
      SimpleDateFormat format = new SimpleDateFormat(yyyyMMddHHmmss);
                Date date = new Date(System.currentTimeMillis());
             filename = format.format(date);
        //創建File對象用於存儲拍照的圖片 SD卡根目錄           
        File path = Environment.getExternalStorageDirectory();

             File outputImage = new File(path,filename+.jpg);
                try {
                    if(outputImage.exists()) {
                        outputImage.delete();
                    }
                    outputImage.createNewFile();
                } catch(IOException e) {
                    e.printStackTrace();
                }
                //將File對象轉換為Uri並啟動照相程序
                imageUri = Uri.fromFile(outputImage);
                Intent cameras = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //照相
                cameras.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //指定圖片輸出地址
                startActivityForResult(cameras,TAKE_PICTURE); //啟動照相
                //拍完照startActivityForResult() 結果返回onActivityResult()函數

                break;
                //開始識別
            case R.id.detect:
                if (mCurrentPhotoPath != null
                        && !mCurrentPhotoPath.trim().equals())
                {
                    resizePhoto();
                } else if(!isCamera)
                {
                    //重置默認圖片
                    mPhotoImg = BitmapFactory.decodeResource(getResources(),
                            R.drawable.level1);
                }

                dialog.show();

//對圖片進行識別               
    FaceRecognize.recognize(mPhotoImg, new CallBack()
                {

                    @Override
                    public void success(JSONObject result)
                    {
                        Message msg = Message.obtain();
                        msg.what = MSG_SUCCESS;
                        msg.obj = result;
                        mHandler.sendMessageDelayed(msg, 500);
                    }

                    @Override
                    public void error(FaceppParseException e)
                    {
                        Message msg = Message.obtain();
                        msg.what = MSG_ERROR;
                        msg.obj = e.getErrorMessage();
                        mHandler.sendMessageDelayed(msg, 500);
                    }
                });
                break;
        }
    }

在OnActivityResult回調方法中,我們分別處理拍照、相冊選擇照片和圖片裁剪等操作。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        switch (requestCode)
        {
            //從相冊中選擇相片
            case PICK_CODE:
                if (data != null)
                {
                    Uri uri = data.getData();
                    Cursor cursor = getContentResolver().query(uri, null, null,
                            null, null);
                    cursor.moveToFirst();
                    int index = cursor
                            .getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                    mCurrentPhotoPath = cursor.getString(index);
                    cursor.close();

                    // 壓縮照片
                    resizePhoto();
                    img.setImageBitmap(mPhotoImg);
                    tip.setText(Detect-->);
                }
                break;
                //照相
    case TAKE_PICTURE:
        if (resultCode == RESULT_OK)
        {

            //我們需要對圖片進行剪裁
            Intent intent = new Intent(com.android.camera.action.CROP); //剪裁
            intent.setDataAndType(imageUri, image/*);
            intent.putExtra(scale, true);
            //設置寬高比例
            intent.putExtra(aspectX, 1);
            intent.putExtra(aspectY, 1);
            //設置裁剪圖片寬高
            intent.putExtra(outputX, 340);
            intent.putExtra(outputY, 340);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
            Toast.makeText(MainActivity.this, 剪裁圖片, Toast.LENGTH_SHORT).show();
            //廣播刷新相冊 
            Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            intentBc.setData(imageUri);     
            this.sendBroadcast(intentBc);    
            startActivityForResult(intent, CROP_PHOTO); //設置裁剪參數顯示圖片至ImageView

        }
                break;
                //剪裁圖片
    case CROP_PHOTO:
                //圖片解析成Bitmap對象
                try
                {
                    //Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
                    mPhotoImg = BitmapFactory.decodeStream(
                            getContentResolver().openInputStream(imageUri));
                    Toast.makeText(MainActivity.this, imageUri.toString(), Toast.LENGTH_SHORT).show();
                    isCamera=true;
                    img.setImageBitmap(mPhotoImg);
                    tip.setText(Detect-->); //將剪裁後照片顯示出來
                } catch (FileNotFoundException e)
                {
                    e.printStackTrace();
                } 

                break;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

注意,從相冊中選擇的圖片,我們需要重新調整其大小,防止其尺寸過大而使得程序崩潰。resizeBitmap方法用於調整圖片大小

//重置圖片的大小 對圖片進行壓縮
    private void resizePhoto()
    {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(mCurrentPhotoPath, options);
        double scaleRatio = Math.max(options.outWidth * 1.0d / 1024f,
                options.outHeight * 1.0d / 1024f);
        options.inSampleSize = (int) Math.ceil(scaleRatio);
        options.inJustDecodeBounds = false;
        mPhotoImg = BitmapFactory.decodeFile(mCurrentPhotoPath, options);
    }

測試

最後 ,對我們的程序進行測試~

這裡寫圖片描述

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved