提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


文章目录

  • 前言
  • 一、人脸检测是什么?
  • 二、使用步骤
  • 1.引入库
  • 2.分包操作
  • 3.添加自定义MyApplication
  • 4.NetworkRequiredInfo
  • 5.AppContext
  • 5.ApiService接口类
  • 人脸识别activity



前言

最近在写一个人脸识别的小练习,现在记录一下


一、人脸检测是什么?

从手机照片获取一张人脸照片,进行检测,调用百度ai人脸检测接口。

二、使用步骤

1.引入库

代码如下(示例):

implementation 'com.orhanobut:hawk:2.0.1'//数据存储
  //图片加载框架
  implementation 'com.github.bumptech.glide:glide:4.11.0'
  implementation 'com.github.bumptech.glide:compiler:4.11.0'
  implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
  implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
  implementation 'io.reactivex.rxjava2:rxjava:2.2.12'
  implementation 'com.squareup.retrofit2:retrofit:2.9.0'
  implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")
  implementation("androidx.fragment:fragment-ktx:1.4.1")

2.分包操作

项目包名如图所示:

android BiometricManager 获取是否设置了人脸解锁 android人脸解锁api_rxjava

3.添加自定义MyApplication

在application中添加一个类名为MyApplication的类

public class MyApplication extends Application {

  static Context context;
  @Override
  public void onCreate() {
    super.onCreate();
    context = getApplicationContext();

    Hawk.init(this).build();
    //初始化
    NetworkApi.init(new NetworkRequiredInfo(this));
  }


  public static synchronized MyApplication context() {
    return (MyApplication) context;
  }

  /**
   *Show Toast  简单封装
   */
  public static void showToastLong(String msg) {
    showToast(context, msg, Toast.LENGTH_LONG);
  }

  public static void showToastShort(String msg) {
    showToast(context, msg, Toast.LENGTH_SHORT);
  }

  public static void showToastShort(int strRes) {
    showToast(context, context.getString(strRes), Toast.LENGTH_SHORT);
  }

  public static void showToast(String msg) {
    showToast(context, msg, Toast.LENGTH_SHORT);
  }

  public static void showToast(int strRes) {
    showToast(context, context.getString(strRes), Toast.LENGTH_SHORT);
  }

  public static void showToastLong(int strRes) {
    showToast(context, context.getString(strRes), Toast.LENGTH_LONG);
  }

  public static void showToast(Context context, String msg, int duration) {
    Toast.makeText(context, msg, duration).show();
  }
}

4.NetworkRequiredInfo

在application中添加一个类名为NetworkRequiredInfo 的类

public class NetworkRequiredInfo implements INetworkRequiredInfo {

  private Application application;

  public NetworkRequiredInfo(Application application){
    this.application = application;
  }

  /**
   * 版本名
   */
  @Override
  public String getAppVersionName() {
    return "1";
  }
  /**
   * 版本号
   */
  @Override
  public String getAppVersionCode() {
    return "1.0";
  }

  /**
   * 是否为debug
   */
  @Override
  public boolean isDebug() {
    return BuildConfig.DEBUG;
  }

  /**
   * 应用全局上下文
   */
  @Override
  public Application getApplicationContext() {
    return application;
  }
}

5.AppContext

在application中添加一个类名为AppContext的类

public class AppContext extends MyApplication {

  private static AppContext instance;

  @Override
  public void onCreate() {
    super.onCreate();
    instance = this;
  }

  /**
   * 获得当前app运行的AppContext
   */
  public static AppContext getInstance() {
    return instance;
  }

  /**
   * ResponseBody 处理成 Json
   */
  public static String doJson(ResponseBody responseBody) {
    long contentLength = responseBody.contentLength();
    BufferedSource source = responseBody.source();
    try {
      source.request(Long.MAX_VALUE); // Buffer the entire body.
    } catch (IOException e) {
      e.printStackTrace();
    }
    Buffer buffer = source.buffer();
    Charset charset = UTF8;
    MediaType contentType = responseBody.contentType();
    if (contentType != null) {
      try {
        charset = contentType.charset(UTF8);
      } catch (UnsupportedCharsetException e) {
        e.printStackTrace();
      }
    }
    String result = "";
    // 拦截器,
    if (contentLength != 0) {
      result = buffer.clone().readString(charset);
      //            Log.e("MainActivity", " doJson====>:" + result);
    }
    return result;
  }

  private static final Charset UTF8 = Charset.forName("UTF-8");

  /***
   * 将指定路径的图片转uri
   * @param context
   * @param path ,指定图片(或文件)的路径
   * @return
   */
@SuppressLint("Range") public static Uri getMediaUriFromPath(Context context, String path) {
    Uri mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    Cursor cursor = context.getContentResolver().query(mediaUri,
        null,
        MediaStore.Images.Media.DISPLAY_NAME + "= ?",
        new String[] { path.substring(path.lastIndexOf("/") + 1) },
        null);

    Uri uri = null;
    if (cursor.moveToFirst()) {
      uri = ContentUris.withAppendedId(mediaUri,
          cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
    }
    cursor.close();
    return uri;
  }

  /**
   * 将相册uri转换成url
   */
  public static String getRealPathFromURI(Context context, Uri contentURI) {
    String result;
    Cursor cursor = context.getContentResolver().query(contentURI,
        new String[] { MediaStore.Images.ImageColumns.DATA },//
        null, null, null);
    if (cursor == null) { result = contentURI.getPath(); } else {
      cursor.moveToFirst();
      int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
      result = cursor.getString(index);
      cursor.close();
    }
    return result;
  }

  /**
   * 将拍照的图片保存到系统相册
   */
  public static String saveImageToGallery(Context context, Bitmap bmp) {
    String fileName = "zhengfutanfang" + ".jpg";
    //检查有没有存储权限
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
      AppContext.showToast("请至权限中心打开应用权限");
      return "saveError";
    } else {
      // 新建目录appDir,并把图片存到其下
      File appDir = new File(context.getExternalFilesDir(null).getPath() + "BarcodeBitmap");
      if (!appDir.exists()) {
        appDir.mkdir();
      }
      File file = new File(appDir, fileName);
      try {
        FileOutputStream fos = new FileOutputStream(file);
        bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
        fos.flush();
        fos.close();
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
      // 把file里面的图片插入到系统相册中
      try {
        MediaStore.Images.Media.insertImage(context.getContentResolver(),
            file.getAbsolutePath(), fileName, null);
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      }

      // 通知相册更新
      context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
      return context.getExternalFilesDir(null).getPath() + "BarcodeBitmap/" + fileName;
    }
  }
}

5.ApiService接口类

在service包中添加一个ApiService接口类

public interface ApiService {

  public static String mBaseUrl = "http://10.0.2.2:8080/web-lesson3-demo/";
  //获取验证码
  @GET("api/validate-code")
  Observable<CodeBean> getCodePicture();

  @GET("api/validate-code")
  Observable<ResponseBody> getCodePicture2();

  //登录接口
  @POST("api/login")
  Observable<ResponseBody> loginApi(@Body RequestBody body);

  //识别脸部照片
  @POST("api/ai-face")
  @Multipart
  Observable<ResponseBody> uploadFacePhoto2(@Part MultipartBody.Part part, @Part("token") RequestBody token);

  //识别脸部照片
  @POST("api/ai-face")
  @Multipart
  Observable<FaceBean> uploadFacePhoto(@Part MultipartBody.Part part, @Part("token") RequestBody token);

}

人脸识别activity

public class FaceDetectionActivity extends BaseActivity<ActivityFaceDetectionBinding> {

  private static final int REQUEST_CODE = 10000;//权限
  private static final int TAKE_PHOTO = 11;// 拍照
  private static final int LOCAL_CROP = 13;// 本地图库

  public final String TAG = this.getClass().getSimpleName();

  String currentFileUrl = "";

  private FaceAdapter faceAdapter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    faceAdapter = new FaceAdapter(this);
    //android版本在6.0以上
    if (Build.VERSION.SDK_INT >= 23) {
      System.out.println("版本正确");
      checkPermission();
    } else {
      System.out.println("版本过低");
    }

    binding.btnChoosePicture.setOnClickListener(view -> getPhoto());
    binding.btnIdentifyPicture.setOnClickListener(view -> identifyFacePhoto());
    binding.btnFaceLogout.setOnClickListener(view -> LogoutUser());
    binding.ivFacepicture.setOnClickListener(view -> getPhoto());
  }

  private void LogoutUser() {
    Hawk.delete("token");
    Intent intent = new Intent(FaceDetectionActivity.this, LoginActivity.class);
    startActivity(intent);
  }

  //获取人脸照片
  private void getPhoto() {
    CharSequence[] items = { "拍照", "图库" };// 裁剪items选项
    // 弹出对话框提示用户拍照或者是通过本地图库选择图片
    new AlertDialog.Builder(this)
        .setItems(items, new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {

            switch (which) {
              // 选择了拍照
              case 0:
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent, TAKE_PHOTO);
                break;
              // 调用系统图库
              case 1:
                // 创建Intent,用于打开手机本地图库选择图片
                Intent intent1 = new Intent(Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                // 启动intent打开本地图库
                startActivityForResult(intent1, LOCAL_CROP);
                break;
            }
          }
        }).show();
  }

  /**
   * 调用startActivityForResult方法启动一个intent后,
   * 可以在该方法中拿到返回的数据
   */
  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
      case TAKE_PHOTO:// 拍照
        if (resultCode == RESULT_OK) {
          final Bitmap photo = data.getParcelableExtra("data");
          //将bitmap照片放入系统相册,并将照片给url
          currentFileUrl = AppContext.saveImageToGallery(this, photo);
          binding.ivFacepicture.setImageBitmap(photo);
          Log.d(TAG, "onActivityResult: " + currentFileUrl);
        }
        break;
      case LOCAL_CROP:// 系统图库
        if (resultCode == RESULT_OK) {
          Uri uri = data.getData();
          binding.ivFacepicture.setImageURI(uri);
          currentFileUrl = AppContext.getRealPathFromURI(this, uri);
          Log.d(TAG, "onActivityResult: " + currentFileUrl);
        }
        break;
    }
  }

  private void identifyFacePhoto() {
    if (TextUtils.isEmpty(currentFileUrl)) {
      AppContext.showToast("请先选择人脸照片");
      return;
    } else {
      binding.progressBar.setVisibility(View.VISIBLE);
      File file = new File(currentFileUrl);
      MultipartBody.Part part = MultipartBody.Part.createFormData("image",
          file.getName(),
          RequestBody.create(MediaType.parse("image/*"),
              file));

      RequestBody body = RequestBody.create(MediaType.parse("text/plain"), "" + Hawk.get("token"));
      NetworkApi.createService(ApiService.class)
          .uploadFacePhoto2(part, body)
          .compose(NetworkApi.applySchedulers(new BaseObserver<ResponseBody>() {
            @Override public void onSuccess(ResponseBody responseBody) {
              String result = AppContext.doJson(responseBody);
              String face_list = null;
              FaceBean.ResultBean faceListBean = null;
              try {
                JSONObject jsonObject = new JSONObject(result);
                face_list = jsonObject.getString("result");
                KLog.d(TAG, face_list);
                Gson gson = new Gson();
                faceListBean = gson.fromJson(String.valueOf(new JSONObject(face_list)), FaceBean.ResultBean.class);
              } catch (JSONException e) {
                e.printStackTrace();
              }
              if (faceListBean != null) {
                showListFace(faceListBean);
                // binding.tvIdentifyFaceRusult.setText(AppContext.doJson(responseBody));
              } else {
                AppContext.showToast("识别返回接口数据为空");
                KLog.d(TAG, "识别返回接口数据为空");

                binding.progressBar.setVisibility(View.GONE);
              }
            }



            @Override public void onFailure(Throwable e) {

              binding.progressBar.setVisibility(View.GONE);
              KLog.d(TAG, e.toString());
              AppContext.showToast("访问失败");
            }
          }));
    }
  }
  
  //测试了一下

  //展示泪飙
  private void showListFace(FaceBean.ResultBean faceListBean) {
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    binding.ryFacePeople.setLayoutManager(layoutManager);
    binding.ryFacePeople.setAdapter(faceAdapter);

    binding.progressBar.setVisibility(View.GONE);
    faceAdapter.updateFaceList(faceListBean.getFace_list());
  }

  private void checkPermission() {
    ActivityCompat.requestPermissions(FaceDetectionActivity.this,
        new String[] { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE },
        REQUEST_CODE);
  }

  @Override
  public void onRequestPermissionsResult(int requestCode,
                                         String permissions[], int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
      //权限的申请结果返回
      case REQUEST_CODE: {
        if (grantResults.length > 0
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
          //已授权
          AppContext.showToast("授权成功");
        } else {
          //未授权
          AppContext.showToast("授权被拒绝");
        }
      }
    }
  }

  @Override public void onBackPressed() {
    super.onBackPressed();
    finish();
  }
}