Repository: MrFuFuFu/RxFace
Branch: master
Commit: f8753b8d76d1
Files: 53
Total size: 114.0 KB
Directory structure:
gitextract_nz7e2i3v/
├── .gitignore
├── .idea/
│ ├── .name
│ ├── compiler.xml
│ ├── copyright/
│ │ └── profiles_settings.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── Face.iml
├── README.md
├── RxFace.iml
├── app/
│ ├── .gitignore
│ ├── app.iml
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── mrfu/
│ │ └── face/
│ │ └── ApplicationTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── mrfu/
│ │ │ └── rxface/
│ │ │ ├── AppApplication.java
│ │ │ ├── BaseActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── business/
│ │ │ │ ├── Constants.java
│ │ │ │ └── DealData.java
│ │ │ ├── loader/
│ │ │ │ ├── ExecutorManager.java
│ │ │ │ ├── FaceApi.java
│ │ │ │ ├── SchedulersCompat.java
│ │ │ │ ├── WebServiceException.java
│ │ │ │ └── custom/
│ │ │ │ ├── AsciiTypeString.java
│ │ │ │ ├── CustomMultipartTypedOutput.java
│ │ │ │ └── CustomTypedByteArray.java
│ │ │ └── models/
│ │ │ ├── BaseResponse.java
│ │ │ ├── FaceResponse.java
│ │ │ └── NeedDataEntity.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ └── content_main.xml
│ │ ├── menu/
│ │ │ └── menu_main.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-v21/
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── test/
│ └── java/
│ └── mrfu/
│ └── face/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── sdk_unuse/
│ ├── HttpRequests.java
│ ├── MainActivity.java
│ └── PostParameters.java
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
================================================
FILE: .idea/.name
================================================
RxFace
================================================
FILE: .idea/compiler.xml
================================================
================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
Android API 21 Platform
================================================
FILE: .idea/modules.xml
================================================
================================================
FILE: .idea/runConfigurations.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: Face.iml
================================================
================================================
FILE: README.md
================================================
RxFace
=====================
用 RxJava, Retrofit, Okhttp 处理人脸识别的简单用例
## Overview
这是一个人脸识别的简单 Demo, 使用了 [FacePlusPlus](http://www.faceplusplus.com.cn/) 的接口。他们的`/detection/detect` 人脸识别接口可以使用普通的 `get` 也可以用 `post` 传递图片二进制流的形式。其中 `post` 的时候遇到了相当多的坑,下面会提。
该 demo 的网络请求库使用了 [Retrofit](https://github.com/square/retrofit) 并集成了 [OkHttp](https://github.com/square/okhttp),使用 [RxJava](https://github.com/ReactiveX/RxJava) 进行封装,方便以流的形式处理网络回调以及图片处理,View 的注入框架用了 [ButterKnife](https://github.com/JakeWharton/butterknife),图片加载使用 [Glide](https://github.com/bumptech/glide)。
## Versions
### v1.1
1. 增加了 compose 复用 work thread 处理数据,然后在 main thread 处理结果的逻辑:你可以在这片文章看到更多:[RxWeekend——RxJava周末狂欢](http://www.jianshu.com/p/ce228f517586)
2. mSubscription.unsubscribe();
3. 增加了Service 返回错误情况的处理
### v1.0
## Main difficulties
当直接使用 `get` 通过传图片 Url 拿到人脸识别数据的话是相当简单的,如下请求链接只要使用 `Retrofit` 的 `get` 请求的 `@QueryMap` 传递参数即可: [Get数据Demo](http://apicn.faceplusplus.com/v2/detection/detect?api_key=7cd1e10dc037bbe9e6db2813d6127475&api_secret=gruCjvStG159LCJutENBt6yzeLK_5ggX&url=http://imglife.gmw.cn/attachement/jpg/site2/20111014/002564a5d7d21002188831.jpg)。
主要存在的困难点是,当获取本地图片,再使用 `post` 传二进制图片数据时,`post` 要使用 `MultipartTypedOutput`,可参考 [stackoverflow](http://stackoverflow.com/questions/25249042/retrofit-multiple-images-attached-in-one-multipart-request/25260556#25260556) 的回答。然而,这样并没有结束,根据 FacePlusPlus 提供的 SDK Sample 里的 Httpurlconnection 的得到的 `post` 请求头是这样的:
`[Content-Disposition: form-data; name="api_key", Content-Type: text/plain; charset=US-ASCII, Content-Transfer-Encoding: 8bit]`
`[Content-Disposition: form-data; name="img"; filename="NoName", Content-Type: application/octet-stream, Content-Transfer-Encoding: binary]`
而使用 `Retrofit` 默认实现的话,我们这样来实现:
```java
public static MultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){
byte[] data = getBitmapByte(bitmap);
MultipartTypedOutput multipartTypedOutput = new MultipartTypedOutput();
multipartTypedOutput.addPart("api_key", new TypedString(Constants.API_KEY));
multipartTypedOutput.addPart("api_secret", new TypedString(Constants.API_SECRET));
multipartTypedOutput.addPart("img", new TypedByteArray("application/octet-stream", data));
return multipartTypedOutput;
}
```
根据 Sample 的请求头,`RestAdapter` 的请求头参数我们这样设置来:
```java
private RequestInterceptor mRequestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("connection", "keep-alive");
request.addHeader("Content-Type", "multipart/form-data; boundary="+ getBoundary() + "; charset=UTF-8");
}
};
```
但是!!!它得到的String参数的头是这样的,这里没有贴出其他的差异,
```java
Content-Disposition: form-data; name="api_key"
Content-Type: text/plain; charset=UTF-8
Content-Length: 32
Content-Transfer-Encoding: 8bit
```
所以需要重写三个类:
`MultipartTypedOutput` 为 final 类,所以重写为 `CustomMultipartTypedOutput`,并使其构造函数,增加 boundary 的设置;
`TypedString `默认的编码格式是`UTF-8`,所以重写为 `AsciiTypeString`类,使其编码格式改为 `US-ASCII`;
`TypedByteArray` 默认的的 `fileName()` 方法返回的是 null,而当传图片数据时需要 fileName 为 "NoName",所以重写为 `CustomTypedByteArray` 类,设置其 fileName 为 "NoName"。
同时需要注意的是在设置 `RestAdapter` 的 header 时,其 boundary 一定要和 `CustomMultipartTypedOutput` 的 boundary 相同,否则服务端无法匹配的!(这个地方,一时没注意,被整了一个多小时才发现!!)
最后 body 的传参,这样来得到:
```java
public static CustomMultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){
byte[] data = getBitmapByte(bitmap);
CustomMultipartTypedOutput multipartTypedOutput = new CustomMultipartTypedOutput(boundary);
multipartTypedOutput.addPart("api_key", "8bit", new AsciiTypeString(Constants.API_KEY));
multipartTypedOutput.addPart("api_secret", "8bit", new AsciiTypeString(Constants.API_SECRET));
multipartTypedOutput.addPart("img", new CustomTypedByteArray("application/octet-stream", data));
return multipartTypedOutput;
}
```
## Preview


## More about me
* [MrFu-傅圆的个人博客](http://mrfu.me/)
## Acknowledgments
* [Glide](https://github.com/bumptech/glide) -Glide
* [Retrofit](https://github.com/square/retrofit) - Retrofit
* [OkHttp](https://github.com/square/okhttp) - OkHttp
* [RxJava](https://github.com/ReactiveX/RxJava) - RxJava
* [ButterKnife](https://github.com/JakeWharton/butterknife) - ButterKnife
License
============
Copyright 2015 MrFu
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
[](https://bitdeli.com/free "Bitdeli Badge")
================================================
FILE: RxFace.iml
================================================
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/app.iml
================================================
generateDebugAndroidTestSources
generateDebugSources
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "mrfu.face"
minSdkVersion 14
targetSdkVersion 23
versionCode 3
versionName "1.1"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
// compileOptions {
// sourceCompatibility JavaVersion.VERSION_1_8
// targetCompatibility JavaVersion.VERSION_1_8
// }
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
compile 'com.squareup.okhttp:okhttp:2.0.0'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
compile 'com.github.bumptech.glide:glide:3.6.0'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/MrFu/Desktop/Android/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/androidTest/java/mrfu/face/ApplicationTest.java
================================================
package mrfu.face;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/mrfu/rxface/AppApplication.java
================================================
package mrfu.rxface;
import android.app.Application;
import android.content.Context;
/**
* Created by MrFu on 15/12/15.
*/
public class AppApplication extends Application {
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/BaseActivity.java
================================================
package mrfu.rxface;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import butterknife.ButterKnife;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
/**
* Created by MrFu on 16/1/10.
*/
public class BaseActivity extends AppCompatActivity {
protected Subscription mSubscription = Subscriptions.empty();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
ButterKnife.bind(BaseActivity.this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(BaseActivity.this);
if (mSubscription != null && !mSubscription.isUnsubscribed()) mSubscription.unsubscribe();
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/MainActivity.java
================================================
package mrfu.rxface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import butterknife.Bind;
import butterknife.OnClick;
import mrfu.rxface.business.Constants;
import mrfu.rxface.business.DealData;
import mrfu.rxface.loader.FaceApi;
import mrfu.rxface.models.FaceResponse;
import mrfu.rxface.models.NeedDataEntity;
import rx.Observable;
import rx.Observer;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
public class MainActivity extends BaseActivity {
@Bind(R.id.iv_face_get)
ImageView iv_face_get;
@Bind(R.id.iv_face_post)
ImageView iv_face_post;
@Bind(R.id.btn_recongize_get)
Button btn_recongize_get;
@Bind(R.id.btn_recongize_post)
Button btn_recongize_post;
@Bind(R.id.tv_age_get)
TextView tv_age_get;
@Bind(R.id.tv_age_post)
TextView tv_age_post;
/**
* 0:GET button
* 1:POST button
*/
private int type = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Glide.with(this).load(Constants.IMAGE_URL).fitCenter().into(iv_face_get);
iv_face_post.setImageResource(R.drawable.jobs);
}
@OnClick(R.id.btn_recongize_get)
public void btn_recongize_get(View view){
type = 0;
Map options = new HashMap<>();
options.put("api_key", Constants.API_KEY);
options.put("api_secret", Constants.API_SECRET);
options.put("url", Constants.IMAGE_URL);
mSubscription = FaceApi.getIns()//api_key={apiKey}&api_secret={apiSecret}&url={imgUrl}
.getDataUrlGet(options)
.observeOn(Schedulers.newThread())
.flatMap(new Func1>() {
@Override
public Observable call(FaceResponse faceResponse) {
Bitmap bitmap = null;
try {
//java.lang.IllegalArgumentException: YOu must call this method on a background thread
bitmap = Glide.with(MainActivity.this).load(Constants.IMAGE_URL).asBitmap().into(-1, -1).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
NeedDataEntity entity = new NeedDataEntity();
entity.bitmap = DealData.drawLineGetBitmap(faceResponse, bitmap);
entity.displayStr = DealData.getDisplayInfo(faceResponse);
return Observable.just(entity);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(setBitmapDataObserver);
}
@OnClick(R.id.btn_recongize_post)
public void btn_recongize_post(View view){
type = 1;
BitmapDrawable mDrawable = (BitmapDrawable) iv_face_post.getDrawable();
final Bitmap bitmap = mDrawable.getBitmap();
mSubscription = FaceApi.getIns()
.getDataPost(DealData.mulipartData(bitmap, FaceApi.getIns().getBoundary()))
.flatMap(new Func1>() {
@Override
public Observable call(FaceResponse faceResponse) {
NeedDataEntity entity = new NeedDataEntity();
entity.bitmap = DealData.drawLineGetBitmap(faceResponse, bitmap);
entity.displayStr = DealData.getDisplayInfo(faceResponse);
return Observable.just(entity);
}
})
.subscribe(setBitmapDataObserver);
}
private Observer setBitmapDataObserver = new Observer() {
@Override
public void onNext(final NeedDataEntity needDataEntity) {
if (needDataEntity == null){
return;
}
switch (type){
case 0:
if (!TextUtils.isEmpty(needDataEntity.displayStr)){
tv_age_get.setText(needDataEntity.displayStr);
}
if (needDataEntity.bitmap != null){
iv_face_get.setImageBitmap(needDataEntity.bitmap);
}
break;
case 1:
if (!TextUtils.isEmpty(needDataEntity.displayStr)){
tv_age_post.setText(needDataEntity.displayStr);
}
if (needDataEntity.bitmap != null){
iv_face_post.setImageBitmap(needDataEntity.bitmap);
}
break;
default:
break;
}
}
@Override
public void onCompleted() {
Log.i("MrFu", "onCompleted");
}
@Override
public void onError(final Throwable error) {
error.printStackTrace();
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
Uri uri = Uri.parse("https://github.com/MrFuFuFu/RxFace");
Intent i = new Intent(Intent.ACTION_VIEW, uri);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/business/Constants.java
================================================
package mrfu.rxface.business;
/**
* Created by MrFu on 15/12/15.
*/
public class Constants {
// public static final String FACE_SERVER_IP = "https://apicn.faceplusplus.com/v2";
public static final String FACE_SERVER_IP = "http://apicn.faceplusplus.com/v2";
public static final String API_KEY = "7cd1e10dc037bbe9e6db2813d6127475";
public static final String API_SECRET = "gruCjvStG159LCJutENBt6yzeLK_5ggX";
public static final String IMAGE_URL = "http://www.ipmm.cn/UploadImage/20130502/2013050209272791.jpg";
public static final int TIME_OUT = 30 * 1000;
}
================================================
FILE: app/src/main/java/mrfu/rxface/business/DealData.java
================================================
package mrfu.rxface.business;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import java.io.ByteArrayOutputStream;
import mrfu.rxface.loader.custom.AsciiTypeString;
import mrfu.rxface.loader.custom.CustomMultipartTypedOutput;
import mrfu.rxface.loader.custom.CustomTypedByteArray;
import mrfu.rxface.models.FaceResponse;
/**
* Created by MrFu on 15/12/15.
*/
public class DealData {
/**
* 设置参数
* 使用 MultipartTypedOutput, 而不使用 Retrofit @Multipart 的 @Part 和 @PartMap 的形式
* http://stackoverflow.com/questions/25249042/retrofit-multiple-images-attached-in-one-multipart-request/25260556#25260556
*
* @see CustomMultipartTypedOutput 重写 MultipartTypedOutput 使之接受 boundary 参数
* @see AsciiTypeString , 重写 TypedByteArray, 使其编码格式为 US-ASCII
* @see CustomTypedByteArray , 重写 TypedByteArray 设置其 fileName 为 "NoName",
* 以上参数格式和参数类型都必须指定,否则会返回对应的错误
* http://www.faceplusplus.com.cn/detection_detect/
* @param bitmap need upload image
* @param boundary must same with http header boundary
* @return http post body param
*/
public static CustomMultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){
byte[] data = getBitmapByte(bitmap);
CustomMultipartTypedOutput multipartTypedOutput = new CustomMultipartTypedOutput(boundary);
multipartTypedOutput.addPart("api_key", "8bit", new AsciiTypeString(Constants.API_KEY));
multipartTypedOutput.addPart("api_secret", "8bit", new AsciiTypeString(Constants.API_SECRET));
multipartTypedOutput.addPart("img", new CustomTypedByteArray("application/octet-stream", data));
return multipartTypedOutput;
}
/**
* convert bitmap to byte[]
* @param bitmap need convert bitmap
* @return byte[]
*/
private static byte[] getBitmapByte(Bitmap bitmap){
ByteArrayOutputStream stream = new ByteArrayOutputStream();
float scale = Math.min(1, Math.min(600f / bitmap.getWidth(), 600f / bitmap.getHeight()));
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
Bitmap imgSmall = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
imgSmall.compress(Bitmap.CompressFormat.JPEG, 100, stream);
return stream.toByteArray();
}
/**
* draw to include face bitmap
* @param entity data
* @param viewBitmap display bitmap
* @return face bitmap
*/
public static Bitmap drawLineGetBitmap(FaceResponse entity, Bitmap viewBitmap){
//use the red paint
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(Math.max(viewBitmap.getWidth(), viewBitmap.getHeight()) / 100f);
//create a new canvas
Bitmap bitmap = Bitmap.createBitmap(viewBitmap.getWidth(), viewBitmap.getHeight(), viewBitmap.getConfig());
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(viewBitmap, new Matrix(), null);
try {
//find out all faces
final int count = entity.face.size();
for (int i = 0; i < count; ++i) {
float x, y, w, h;
//get the center point
x = (float) entity.face.get(i).position.center.x;
y = (float) entity.face.get(i).position.center.y;
//get face size
w = (float) entity.face.get(i).position.width;
h = (float) entity.face.get(i).position.height;
//change percent value to the real size
x = x / 100 * viewBitmap.getWidth();
w = w / 100 * viewBitmap.getWidth() * 0.7f;
y = y / 100 * viewBitmap.getHeight();
h = h / 100 * viewBitmap.getHeight() * 0.7f;
//draw the box to mark it out
canvas.drawLine(x - w, y - h, x - w, y + h, paint);
canvas.drawLine(x - w, y - h, x + w, y - h, paint);
canvas.drawLine(x + w, y + h, x - w, y + h, paint);
canvas.drawLine(x + w, y + h, x + w, y - h, paint);
}
//save new image
viewBitmap = bitmap;
return viewBitmap;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public static String getDisplayInfo(FaceResponse entity){
if (entity == null || entity.face == null || entity.face.size() == 0
|| entity.face.get(0).attribute == null){
return "";
}
int age = 0;
if (entity.face.get(0).attribute.age != null){
age = entity.face.get(0).attribute.age.value;
}
String gender = "";
if (entity.face.get(0).attribute.gender != null){
gender = "male".equalsIgnoreCase(entity.face.get(0).attribute.gender.value) ? "男" : "女";
}
return "年龄约 " + age + " 性别为 " + gender;
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/ExecutorManager.java
================================================
package mrfu.rxface.loader;
import android.os.Build;
import android.support.annotation.NonNull;
import java.io.File;
import java.io.FileFilter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Joker on 2015/8/24.
*/
public class ExecutorManager {
public static final int DEVICE_INFO_UNKNOWN = 0;
public static ExecutorService eventExecutor;
//private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CPU_COUNT = ExecutorManager.getCountOfCPU();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue eventPoolWaitQueue = new LinkedBlockingQueue<>(128);
private static final ThreadFactory eventThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(@NonNull Runnable r) {
return new Thread(r, "eventAsyncAndBackground #" + mCount.getAndIncrement());
}
};
private static final RejectedExecutionHandler eventHandler =
new ThreadPoolExecutor.CallerRunsPolicy();
static {
eventExecutor =
new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
eventPoolWaitQueue, eventThreadFactory, eventHandler);
}
/**
* Linux中的设备都是以文件的形式存在,CPU也不例外,因此CPU的文件个数就等价与核数。
* Android的CPU 设备文件位于/sys/devices/system/cpu/目录,文件名的的格式为cpu\d+。
*
* 引用:http://www.jianshu.com/p/f7add443cd32#,感谢 liangfeizc :)
* https://github.com/facebook/device-year-class
*/
public static int getCountOfCPU() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
return 1;
}
int count;
try {
count = new File("/sys/devices/system/cpu/").listFiles(CPU_FILTER).length;
} catch (SecurityException | NullPointerException e) {
count = DEVICE_INFO_UNKNOWN;
}
return count;
}
private static final FileFilter CPU_FILTER = new FileFilter() {
@Override public boolean accept(File pathname) {
String path = pathname.getName();
if (path.startsWith("cpu")) {
for (int i = 3; i < path.length(); i++) {
if (path.charAt(i) < '0' || path.charAt(i) > '9') {
return false;
}
}
return true;
}
return false;
}
};
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/FaceApi.java
================================================
package mrfu.rxface.loader;
import com.squareup.okhttp.OkHttpClient;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import mrfu.rxface.BuildConfig;
import mrfu.rxface.business.Constants;
import mrfu.rxface.loader.custom.CustomMultipartTypedOutput;
import mrfu.rxface.models.FaceResponse;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.client.OkClient;
import retrofit.http.Body;
import retrofit.http.GET;
import retrofit.http.POST;
import retrofit.http.QueryMap;
import rx.Observable;
import rx.functions.Func1;
/**
* face api
* Created by MrFu on 15/12/15.
*/
public class FaceApi {
private static String mBoundry;
private final static int boundaryLength = 32;
private final static String boundaryAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
public static FaceApi instance;
public static FaceApi getIns() {
if (null == instance) {
synchronized (FaceApi.class) {
if (null == instance) {
instance = new FaceApi();
}
}
}
return instance;
}
private final MrFuService mWebService;
public FaceApi() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.FACE_SERVER_IP)
.setClient(new OkClient(new OkHttpClient()))
.setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)
.setRequestInterceptor(mRequestInterceptor)
.build();
mWebService = restAdapter.create(MrFuService.class);
mBoundry = setBoundary();
}
private RequestInterceptor mRequestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("connection", "keep-alive");
request.addHeader("Content-Type", "multipart/form-data; boundary="+ getBoundary() + "; charset=UTF-8");
}
};
public String getBoundary(){
return mBoundry;
}
/**
* 设置 Content-Type 的 boundary,这里有个强坑:
* header 的 boundary 和 CustomMultipartTypedOutput 的 boundary 必须相同!!
* @return mBoundry
*/
private static String setBoundary() {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < boundaryLength; ++i)
sb.append(boundaryAlphabet.charAt(random.nextInt(boundaryAlphabet.length())));
return sb.toString();
}
public interface MrFuService {
@POST("/detection/detect")
Observable uploadImagePost(
@Body CustomMultipartTypedOutput listMultipartOutput
);
//http://apicn.faceplusplus.com/v2/detection/detect?api_key=7cd1e10dc037bbe9e6db2813d6127475&api_secret=gruCjvStG159LCJutENBt6yzeLK_5ggX&url=http://imglife.gmw.cn/attachement/jpg/site2/20111014/002564a5d7d21002188831.jpg
@GET("/detection/detect")
Observable uploadImageUrlGet(
@QueryMap Map options
);
}
public Observable getDataPost(CustomMultipartTypedOutput listMultipartOutput) {
return mWebService.uploadImagePost(listMultipartOutput)
.timeout(Constants.TIME_OUT, TimeUnit.MILLISECONDS)
.concatMap(new Func1>() {
@Override
public Observable call(FaceResponse faceResponse) {
return faceResponse.filterWebServiceErrors();
}
}).compose(SchedulersCompat.applyExecutorSchedulers());
}
public Observable getDataUrlGet(Map options) {
return mWebService.uploadImageUrlGet(options)
.timeout(Constants.TIME_OUT, TimeUnit.MILLISECONDS)
.concatMap(new Func1>() {
@Override
public Observable call(FaceResponse faceResponse) {
return faceResponse.filterWebServiceErrors();
}
}).compose(SchedulersCompat.applyExecutorSchedulers());//http://www.jianshu.com/p/e9e03194199e
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/SchedulersCompat.java
================================================
package mrfu.rxface.loader;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* 小鄧子:【译】避免打断链式结构:使用.compose( )操作符 http://www.jianshu.com/p/e9e03194199e
* Created by Joker on 2015/8/10.
*/
public class SchedulersCompat {
private static final Observable.Transformer computationTransformer =
new Observable.Transformer() {
@Override public Object call(Object observable) {
return ((Observable) observable).subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread());
}
};
private static final Observable.Transformer ioTransformer = new Observable.Transformer() {
@Override public Object call(Object observable) {
return ((Observable) observable).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
private static final Observable.Transformer newTransformer = new Observable.Transformer() {
@Override public Object call(Object observable) {
return ((Observable) observable).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
}
};
private static final Observable.Transformer trampolineTransformer = new Observable.Transformer() {
@Override public Object call(Object observable) {
return ((Observable) observable).subscribeOn(Schedulers.trampoline())
.observeOn(AndroidSchedulers.mainThread());
}
};
private static final Observable.Transformer executorTransformer = new Observable.Transformer() {
@Override public Object call(Object observable) {
return ((Observable) observable).subscribeOn(Schedulers.from(ExecutorManager.eventExecutor))
.observeOn(AndroidSchedulers.mainThread());
}
};
/**
* Don't break the chain: use RxJava's compose() operator
*/
public static Observable.Transformer applyComputationSchedulers() {
return (Observable.Transformer) computationTransformer;
}
public static Observable.Transformer applyIoSchedulers() {
return (Observable.Transformer) ioTransformer;
}
public static Observable.Transformer applyNewSchedulers() {
return (Observable.Transformer) newTransformer;
}
public static Observable.Transformer applyTrampolineSchedulers() {
return (Observable.Transformer) trampolineTransformer;
}
public static Observable.Transformer applyExecutorSchedulers() {
return (Observable.Transformer) executorTransformer;
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/WebServiceException.java
================================================
package mrfu.rxface.loader;
/**
* Created by MrFu on 16/1/10.
*/
public class WebServiceException extends Exception {
public WebServiceException(String detailMessage) {
super(detailMessage);
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/custom/AsciiTypeString.java
================================================
package mrfu.rxface.loader.custom;
import java.io.UnsupportedEncodingException;
import retrofit.mime.TypedByteArray;
/**
* 重写 TypedByteArray, 使其编码格式为 US-ASCII
* Created by MrFu on 15/12/16.
*/
public class AsciiTypeString extends TypedByteArray {
public AsciiTypeString(String string) {
super("text/plain; charset=US-ASCII", convertToBytes(string));
}
private static byte[] convertToBytes(String string) {
try {
return string.getBytes("US-ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override public String toString() {
try {
return "TypedString[" + new String(getBytes(), "US-ASCII") + "]";
} catch (UnsupportedEncodingException e) {
throw new AssertionError("Must be able to decode US-ASCII");
}
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/custom/CustomMultipartTypedOutput.java
================================================
package mrfu.rxface.loader.custom;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import retrofit.mime.TypedOutput;
/**
* 重写 MultipartTypedOutput 使之接受 boundary 参数
* Created by MrFu on 15/12/16.
*/
public class CustomMultipartTypedOutput implements TypedOutput {
public static final String DEFAULT_TRANSFER_ENCODING = "binary";
private static final class MimePart {
private final TypedOutput body;
private final String name;
private final String transferEncoding;
private final boolean isFirst;
private final String boundary;
private byte[] partBoundary;
private byte[] partHeader;
private boolean isBuilt;
public MimePart(String name, String transferEncoding, TypedOutput body, String boundary,
boolean isFirst) {
this.name = name;
this.transferEncoding = transferEncoding;
this.body = body;
this.isFirst = isFirst;
this.boundary = boundary;
}
public void writeTo(OutputStream out) throws IOException {
build();
out.write(partBoundary);
out.write(partHeader);
body.writeTo(out);
}
public long size() {
build();
if (body.length() > -1) {
return body.length() + partBoundary.length + partHeader.length;
} else {
return -1;
}
}
private void build() {
if (isBuilt) return;
partBoundary = buildBoundary(boundary, isFirst, false);
partHeader = buildHeader(name, transferEncoding, body);
isBuilt = true;
}
}
private final List mimeParts = new LinkedList();
private final byte[] footer;
private final String boundary;
private long length;
public CustomMultipartTypedOutput() {
this(UUID.randomUUID().toString());
}
public CustomMultipartTypedOutput(String boundary) {
this.boundary = boundary;
footer = buildBoundary(boundary, false, true);
length = footer.length;
}
List getParts() throws IOException {
List parts = new ArrayList(mimeParts.size());
for (MimePart part : mimeParts) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
part.writeTo(bos);
parts.add(bos.toByteArray());
}
return parts;
}
public void addPart(String name, TypedOutput body) {
addPart(name, DEFAULT_TRANSFER_ENCODING, body);
}
public void addPart(String name, String transferEncoding, TypedOutput body) {
if (name == null) {
throw new NullPointerException("Part name must not be null.");
}
if (transferEncoding == null) {
throw new NullPointerException("Transfer encoding must not be null.");
}
if (body == null) {
throw new NullPointerException("Part body must not be null.");
}
MimePart part = new MimePart(name, transferEncoding, body, boundary, mimeParts.isEmpty());
mimeParts.add(part);
long size = part.size();
if (size == -1) {
length = -1;
} else if (length != -1) {
length += size;
}
}
public int getPartCount() {
return mimeParts.size();
}
@Override public String fileName() {
return null;
}
@Override public String mimeType() {
return "multipart/form-data; boundary=" + boundary;
}
@Override public long length() {
return length;
}
@Override public void writeTo(OutputStream out) throws IOException {
for (MimePart part : mimeParts) {
part.writeTo(out);
}
out.write(footer);
}
private static byte[] buildBoundary(String boundary, boolean first, boolean last) {
try {
// Pre-size for the last boundary, the worst case scenario.
StringBuilder sb = new StringBuilder(boundary.length() + 8);
if (!first) {
sb.append("\r\n");
}
sb.append("--");
sb.append(boundary);
if (last) {
sb.append("--");
}
sb.append("\r\n");
return sb.toString().getBytes("UTF-8");
} catch (IOException ex) {
throw new RuntimeException("Unable to write multipart boundary", ex);
}
}
private static byte[] buildHeader(String name, String transferEncoding, TypedOutput value) {
try {
// Initial size estimate based on always-present strings and conservative value lengths.
StringBuilder headers = new StringBuilder(128);
headers.append("Content-Disposition: form-data; name=\"");
headers.append(name);
String fileName = value.fileName();
if (fileName != null) {
headers.append("\"; filename=\"");
headers.append(fileName);
}
headers.append("\"\r\nContent-Type: ");
headers.append(value.mimeType());
long length = value.length();
if (length != -1) {
headers.append("\r\nContent-Length: ").append(length);
}
headers.append("\r\nContent-Transfer-Encoding: ");
headers.append(transferEncoding);
headers.append("\r\n\r\n");
return headers.toString().getBytes("UTF-8");
} catch (IOException ex) {
throw new RuntimeException("Unable to write multipart header", ex);
}
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/loader/custom/CustomTypedByteArray.java
================================================
package mrfu.rxface.loader.custom;
import retrofit.mime.TypedByteArray;
/**
* 重写 TypedByteArray 设置其 fileName 为 "NoName",
* Created by MrFu on 15/12/16.
*/
public class CustomTypedByteArray extends TypedByteArray {
/**
* Constructs a new typed byte array. Sets mimeType to {@code application/unknown} if absent.
*
* @param mimeType
* @param bytes
* @throws NullPointerException if bytes are null
*/
public CustomTypedByteArray(String mimeType, byte[] bytes) {
super(mimeType, bytes);
}
@Override
public String fileName() {
return "NoName";
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/models/BaseResponse.java
================================================
package mrfu.rxface.models;
import mrfu.rxface.loader.WebServiceException;
import rx.Observable;
/**
* Created by MrFu on 16/1/10.
*/
public class BaseResponse {
public Observable filterWebServiceErrors() {
if (true){//judge result status is ok~~
return Observable.just(this);
}else {
return Observable.error(new WebServiceException("Service return Error message"));
}
//demo code just like blow, the common is a class object, you can deal the error code in here.
// public class BaseResponse {
// public Common common;
//
// public Observable filterWebServiceErrors() {
// if (common == null){
// return Observable.error(new WebServiceException("啊喔,服务器除了点小问题"));
// }else {
// int code = Integer.parseInt(common.status);
// switch (code){
// case Constants.RESULT_OK://正常
// return Observable.just(this);
// case Constants.RESULT_NORMAL_UPDATE://普通升级
// case Constants.RESULT_FORCE_UPDATE://墙纸升级
// if (AppApplication.getInstance().updateModel == null) {
// AppApplication.getInstance().updateModel = common.update;
// AppApplication.getInstance().updateModel.code = code;
// }
// return Observable.just(this);
// default://出错
// return Observable.error(new WebServiceException(BaseResponse.this.common.memo));
// }
// }
// }
// }
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/models/FaceResponse.java
================================================
package mrfu.rxface.models;
import java.util.List;
/**
* Created by MrFu on 15/12/16.
*/
public class FaceResponse extends BaseResponse{
/**
* face : [{"attribute":{"age":{"range":6,"value":18},"gender":{"confidence":99.9996,"value":"Male"},"race":{"confidence":99.8977,"value":"White"},"smiling":{"value":81.2229}},"face_id":"5bf244c54d5731974e25ee024b950cd3","position":{"center":{"x":47.833333,"y":49.036403},"eye_left":{"x":42.140333,"y":42.28394},"eye_right":{"x":53.658,"y":42.389936},"height":31.263383,"mouth_left":{"x":42.352167,"y":56.311991},"mouth_right":{"x":53.747833,"y":56.89379},"nose":{"x":47.392667,"y":51.794004},"width":24.333333},"tag":""},{"attribute":{"age":{"range":5,"value":24},"gender":{"confidence":99.5758,"value":"Male"},"race":{"confidence":99.94800000000001,"value":"White"},"smiling":{"value":93.0865}},"face_id":"092cb7660d3f8d115b8af331465624a1","position":{"center":{"x":83.833333,"y":52.462527},"eye_left":{"x":77.052667,"y":47.005353},"eye_right":{"x":87.198,"y":44.253961},"height":28.265525,"mouth_left":{"x":77.304167,"y":60.063169},"mouth_right":{"x":86.635167,"y":60.254176},"nose":{"x":86.9965,"y":54.840685},"width":22},"tag":""},{"attribute":{"age":{"range":11,"value":38},"gender":{"confidence":74.2956,"value":"Female"},"race":{"confidence":96.8155,"value":"White"},"smiling":{"value":86.556}},"face_id":"3c9d898f732c84d07d760f184bfec814","position":{"center":{"x":13,"y":52.248394},"eye_left":{"x":8.483217,"y":44.584797},"eye_right":{"x":18.669667,"y":46.517987},"height":27.837259,"mouth_left":{"x":8.44005,"y":60.162955},"mouth_right":{"x":17.914333,"y":60.197645},"nose":{"x":8.59085,"y":53.623769},"width":21.666667},"tag":""}]
* img_height : 490
* img_id : f3f8c2826537ce51ca1995143e8b9289
* img_width : 629
* session_id : a7f871065a064bdfabe06de48189dcac
* url : http://imglife.gmw.cn/attachement/jpg/site2/20111014/002564a5d7d21002188831.jpg
*/
public int img_height;
public String img_id;
public int img_width;
public String session_id;
public String url;
/**
* attribute : {"age":{"range":6,"value":18},"gender":{"confidence":99.9996,"value":"Male"},"race":{"confidence":99.8977,"value":"White"},"smiling":{"value":81.2229}}
* face_id : 5bf244c54d5731974e25ee024b950cd3
* position : {"center":{"x":47.833333,"y":49.036403},"eye_left":{"x":42.140333,"y":42.28394},"eye_right":{"x":53.658,"y":42.389936},"height":31.263383,"mouth_left":{"x":42.352167,"y":56.311991},"mouth_right":{"x":53.747833,"y":56.89379},"nose":{"x":47.392667,"y":51.794004},"width":24.333333}
* tag :
*/
public List face;
public static class FaceEntity {
/**
* age : {"range":6,"value":18}
* gender : {"confidence":99.9996,"value":"Male"}
* race : {"confidence":99.8977,"value":"White"}
* smiling : {"value":81.2229}
*/
public AttributeEntity attribute;
public String face_id;
/**
* center : {"x":47.833333,"y":49.036403}
* eye_left : {"x":42.140333,"y":42.28394}
* eye_right : {"x":53.658,"y":42.389936}
* height : 31.263383
* mouth_left : {"x":42.352167,"y":56.311991}
* mouth_right : {"x":53.747833,"y":56.89379}
* nose : {"x":47.392667,"y":51.794004}
* width : 24.333333
*/
public PositionEntity position;
public String tag;
public static class AttributeEntity {
/**
* range : 6
* value : 18
*/
public AgeEntity age;
/**
* confidence : 99.9996
* value : Male
*/
public GenderEntity gender;
/**
* confidence : 99.8977
* value : White
*/
public RaceEntity race;
/**
* value : 81.2229
*/
public SmilingEntity smiling;
public static class AgeEntity {
public int range;
public int value;
}
public static class GenderEntity {
public double confidence;
public String value;
}
public static class RaceEntity {
public double confidence;
public String value;
}
public static class SmilingEntity {
public double value;
}
}
public static class PositionEntity {
/**
* x : 47.833333
* y : 49.036403
*/
public CenterEntity center;
/**
* x : 42.140333
* y : 42.28394
*/
public EyeLeftEntity eye_left;
/**
* x : 53.658
* y : 42.389936
*/
public EyeRightEntity eye_right;
public double height;
/**
* x : 42.352167
* y : 56.311991
*/
public MouthLeftEntity mouth_left;
/**
* x : 53.747833
* y : 56.89379
*/
public MouthRightEntity mouth_right;
/**
* x : 47.392667
* y : 51.794004
*/
public NoseEntity nose;
public double width;
public static class CenterEntity {
public double x;
public double y;
}
public static class EyeLeftEntity {
public double x;
public double y;
}
public static class EyeRightEntity {
public double x;
public double y;
}
public static class MouthLeftEntity {
public double x;
public double y;
}
public static class MouthRightEntity {
public double x;
public double y;
}
public static class NoseEntity {
public double x;
public double y;
}
}
}
}
================================================
FILE: app/src/main/java/mrfu/rxface/models/NeedDataEntity.java
================================================
package mrfu.rxface.models;
import android.graphics.Bitmap;
/**
* Created by MrFu on 15/12/16.
*/
public class NeedDataEntity {
public Bitmap bitmap;
public String displayStr;
}
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/content_main.xml
================================================
================================================
FILE: app/src/main/res/menu/menu_main.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp
16dp
16dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
RxFace
Settings
Github
Get识别
Post识别
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-v21/styles.xml
================================================
>
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: app/src/test/java/mrfu/face/ExampleUnitTest.java
================================================
package mrfu.face;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
// classpath 'me.tatarka:gradle-retrolambda:3.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Dec 15 21:05:14 CST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: sdk_unuse/HttpRequests.java
================================================
package mrfu.face.sdk;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.json.JSONException;
import org.json.JSONObject;
import com.facepp.error.FaceppParseException;
/**
* request to faceplusplus.com
* {@code new HttpRequests(apiKey, apiSecret).request("detection", "detect", postParameters)}
*
* {@code new HttpRequests(apiKey, apiSecret).train()}
* @author moon5ckq
* @since 1.0.0
* @version 1.3.0
*/
public class HttpRequests {
static final private String WEBSITE_CN = "https://apicn.faceplusplus.com/v2/";
static final private String DWEBSITE_CN = "http://apicn.faceplusplus.com/v2/";
static final private String WEBSITE_US = "https://apius.faceplusplus.com/v2/";
static final private String DWEBSITE_US = "http://apius.faceplusplus.com/v2/";
static final private int BUFFERSIZE = 1048576;
static final private int TIMEOUT = 30000;
static final private int SYNC_TIMEOUT = 60000;
private String webSite;
private String apiKey, apiSecret;
private PostParameters params;
private int httpTimeOut = TIMEOUT;
/**
* default is 30 sec
* set http timeout limit (million second)
* @param timeOut
*/
public void setHttpTimeOut(int timeOut) {
this.httpTimeOut = timeOut;
}
/**
* (million second)
* @return http timeout limit
*/
public int getHttpTimeOut() {
return this.httpTimeOut;
}
/**
* @return api_key
*/
public String getApiKey() {
return apiKey;
}
/**
* @param apiKey
*/
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
/**
* @return api_secret
*/
public String getApiSecret() {
return apiSecret;
}
/**
* @param apiSecret
*/
public void setApiSecret(String apiSecret) {
this.apiSecret = apiSecret;
}
/**
* if isCN is true, then use AliCloud, false to Amazon
* if isDebug is true, then use http, otherwise https
* @param isCN
* @param isDebug
*/
public void setWebSite(boolean isCN, boolean isDebug) {
if (isCN && isDebug) webSite = DWEBSITE_CN;
else if (isCN && !isDebug) webSite = WEBSITE_CN;
else if (!isCN && isDebug) webSite = DWEBSITE_US;
else if (!isCN && !isDebug) webSite = WEBSITE_US;
}
/**
* @return a webSite clone
*/
public String getWebSite() {
return new String(webSite);
}
/**
* {@link #request(String, String, PostParameters)}
* faceplusplus.com/[control]/[action]
* default use parameters which {@link #getParams}
* @param control
* @param action
* @return a result object
*/
public JSONObject request(String control, String action) throws FaceppParseException {
return request(control, action, getParams());
}
/**
* default timeout time is 1 minute
* @param sessionId
* @return the getSession Result
* @throws FaceppParseException
*/
public JSONObject getSessionSync(String sessionId) throws FaceppParseException {
return getSessionSync(sessionId, SYNC_TIMEOUT);
}
/**
* timeout time is [timeOut]ms, the method is synchronized.
* @param sessionId
* @param timeOut
* @return the getSession Result
* @throws FaceppParseException
*/
public JSONObject getSessionSync(String sessionId, long timeOut) throws FaceppParseException {
final StringBuilder sb = new StringBuilder();
long t = new Date().getTime() + timeOut;
while (true) {
JSONObject rst = HttpRequests.this.request("info", "get_session", new PostParameters().setSessionId(sessionId));
try {
if (rst.getString("status").equals("SUCC")) {
sb.append(rst.toString());
break;
} else if (rst.getString("status").equals("INVALID_SESSION")) {
sb.append("INVALID_SESSION");
break;
}
} catch (JSONException e) {
sb.append("Unknow error.");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
sb.append("Thread.sleep error.");
break;
}
if (new Date().getTime() >= t) {
sb.append("Time Out");
break;
}
}
String rst = sb.toString();
if (rst.equals("INVALID_SESSION")) {
throw new FaceppParseException("Invaild session, unknow error.");
} else if (rst.equals("Unknow error.")) {
throw new FaceppParseException("Unknow error.");
} else if (rst.equals("Thread.sleep error.")) {
throw new FaceppParseException("Thread.sleep error.");
} else if (rst.equals("Time Out")) {
throw new FaceppParseException("Get session time out.");
} else {
try {
JSONObject result = new JSONObject(rst);
result.put("response_code", 200);
return result;
} catch (JSONException e) {
}
}
return null;
}
/**
* faceplusplus.com/[control]/[action]?[params]
* http request timeout time is 5000ms
* @param control
* @param action
* @param params
* @return a result object
* @throws FaceppParseException
*/
public JSONObject request(String control, String action, PostParameters params) throws FaceppParseException {
URL url;
HttpURLConnection urlConn = null;
try {
url = new URL(webSite+control+"/"+action);
urlConn = (HttpURLConnection) url.openConnection();
urlConn.setRequestMethod("POST");
urlConn.setConnectTimeout(httpTimeOut);
urlConn.setReadTimeout(httpTimeOut);
urlConn.setDoOutput(true);
urlConn.setRequestProperty("connection", "keep-alive");
urlConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + params.boundaryString());
MultipartEntity reqEntity =params.getMultiPart();
reqEntity.addPart("api_key", new StringBody(apiKey));
reqEntity.addPart("api_secret", new StringBody(apiSecret));
reqEntity.writeTo(urlConn.getOutputStream());
String resultString = null;
if (urlConn.getResponseCode() == 200)
resultString = readString(urlConn.getInputStream());
else
resultString = readString(urlConn.getErrorStream());
//FaceppResult result = new FaceppResult( new JSONObject(resultString), urlConn.getResponseCode());
JSONObject result = new JSONObject(resultString);
if (result.has("error")) {
if (result.getString("error").equals("API not found"))
throw new FaceppParseException("API not found");
throw new FaceppParseException("API error.", result.getInt("error_code"),
result.getString("error"), urlConn.getResponseCode());
}
result.put("response_code", urlConn.getResponseCode());
urlConn.getInputStream().close();
return result;
} catch (Exception e) { throw new FaceppParseException("error :" + e.toString());
} finally { if (urlConn != null)
urlConn.disconnect();
}
}
private static String readString(InputStream is) {
StringBuffer rst = new StringBuffer();
byte[] buffer = new byte[BUFFERSIZE];
int len = 0;
try {
while ((len = is.read(buffer)) > 0)
for (int i = 0; i < len; ++i)
rst.append((char)buffer[i]);
} catch (IOException e) {
e.printStackTrace();
}
return rst.toString();
}
/**
* create {@link HttpRequests}
* api_key=...,api_secret=...
* use https and AliCloud default
* @param apiKey
* @param apiSecret
*/
public HttpRequests(String apiKey, String apiSecret) {
super();
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.webSite = WEBSITE_CN;
}
/**
* use https default
* create a empty {@link HttpRequests} object
*/
public HttpRequests() {
super();
}
/**
* create {@link HttpRequests}
* api_key=...,api_secret=...
* the isCN and isDebug use like {@link setWebSite}}
* @param apiKey
* @param apiSecret
* @param isCN
* @param isDebug
*/
public HttpRequests(String apiKey, String apiSecret, boolean isCN, boolean isDebug) {
super();
this.apiKey = apiKey;
this.apiSecret = apiSecret;
setWebSite(isCN, isDebug);
}
/**
* @return {@link PostParameters} object
*/
public PostParameters getParams() {
if (params == null) params = new PostParameters();
return params;
}
/**
* set default PostParameters
* @param params
*/
public void setParams(PostParameters params) {
this.params = params;
}
/**
* used by offline detect
* example: request.offlineDetect(detecter.getImageByteArray(), detecter.getResultJsonString());
* @param image
* @param jsonResult
* @return
* @throws FaceppParseException
*/
public JSONObject offlineDetect(byte[] image, String jsonResult) throws FaceppParseException {
return offlineDetect(image, jsonResult, this.params);
}
/**
* used by offline detect
* example: request.offlineDetect(detecter.getImageByteArray(), detecter.getResultJsonString(), params);
* @param image
* @param jsonResult
* @param params
* @return
* @throws FaceppParseException
*/
public JSONObject offlineDetect(byte[] image, String jsonResult, PostParameters params) throws FaceppParseException{
if (params == null) params = new PostParameters();
params.setImg(image);
params.setMode("offline");
params.addAttribute("offline_result", jsonResult);
return request("detection", "detect", params);
}
//all api here
public JSONObject detectionDetect() throws FaceppParseException {
return request("detection", "detect");
}
public JSONObject detectionDetect(PostParameters params) throws FaceppParseException{
return request("detection", "detect", params);
}
public JSONObject detectionLandmark() throws FaceppParseException {
return request("detection", "landmark");
}
public JSONObject detectionLandmark(PostParameters params) throws FaceppParseException{
return request("detection", "landmark", params);
}
public JSONObject trainVerify() throws FaceppParseException {
return request("train", "verify");
}
public JSONObject trainVerify(PostParameters params) throws FaceppParseException{
return request("train", "verify", params);
}
public JSONObject trainSearch() throws FaceppParseException {
return request("train", "search");
}
public JSONObject trainSearch(PostParameters params) throws FaceppParseException{
return request("train", "search", params);
}
public JSONObject trainIdentify() throws FaceppParseException {
return request("train", "identify");
}
public JSONObject trainIdentify(PostParameters params) throws FaceppParseException{
return request("train", "identify", params);
}
public JSONObject recognitionCompare() throws FaceppParseException {
return request("recognition", "compare");
}
public JSONObject recognitionCompare(PostParameters params) throws FaceppParseException{
return request("recognition", "compare", params);
}
public JSONObject recognitionVerify() throws FaceppParseException {
return request("recognition", "verify");
}
public JSONObject recognitionVerify(PostParameters params) throws FaceppParseException{
return request("recognition", "verify", params);
}
public JSONObject recognitionSearch() throws FaceppParseException {
return request("recognition", "search");
}
public JSONObject recognitionSearch(PostParameters params) throws FaceppParseException{
return request("recognition", "search", params);
}
public JSONObject recognitionIdentify() throws FaceppParseException {
return request("recognition", "identify");
}
public JSONObject recognitionIdentify(PostParameters params) throws FaceppParseException{
return request("recognition", "identify", params);
}
public JSONObject groupingGrouping() throws FaceppParseException {
return request("grouping", "grouping");
}
public JSONObject groupingGrouping(PostParameters params) throws FaceppParseException{
return request("grouping", "grouping", params);
}
public JSONObject personCreate() throws FaceppParseException {
return request("person", "create");
}
public JSONObject personCreate(PostParameters params) throws FaceppParseException{
return request("person", "create", params);
}
public JSONObject personDelete() throws FaceppParseException {
return request("person", "delete");
}
public JSONObject personDelete(PostParameters params) throws FaceppParseException{
return request("person", "delete", params);
}
public JSONObject personAddFace() throws FaceppParseException {
return request("person", "add_face");
}
public JSONObject personAddFace(PostParameters params) throws FaceppParseException{
return request("person", "add_face", params);
}
public JSONObject personRemoveFace() throws FaceppParseException {
return request("person", "remove_face");
}
public JSONObject personRemoveFace(PostParameters params) throws FaceppParseException{
return request("person", "remove_face", params);
}
public JSONObject personSetInfo() throws FaceppParseException {
return request("person", "set_info");
}
public JSONObject personSetInfo(PostParameters params) throws FaceppParseException{
return request("person", "set_info", params);
}
public JSONObject personGetInfo() throws FaceppParseException {
return request("person", "get_info");
}
public JSONObject personGetInfo(PostParameters params) throws FaceppParseException{
return request("person", "get_info", params);
}
public JSONObject facesetCreate() throws FaceppParseException {
return request("faceset", "create");
}
public JSONObject facesetCreate(PostParameters params) throws FaceppParseException{
return request("faceset", "create", params);
}
public JSONObject facesetDelete() throws FaceppParseException {
return request("faceset", "delete");
}
public JSONObject facesetDelete(PostParameters params) throws FaceppParseException{
return request("faceset", "delete", params);
}
public JSONObject facesetAddFace() throws FaceppParseException {
return request("faceset", "add_face");
}
public JSONObject facesetAddFace(PostParameters params) throws FaceppParseException{
return request("faceset", "add_face", params);
}
public JSONObject facesetRemoveFace() throws FaceppParseException {
return request("faceset", "remove_face");
}
public JSONObject facesetRemoveFace(PostParameters params) throws FaceppParseException{
return request("faceset", "remove_face", params);
}
public JSONObject facesetSetInfo() throws FaceppParseException {
return request("faceset", "set_info");
}
public JSONObject facesetSetInfo(PostParameters params) throws FaceppParseException{
return request("faceset", "set_info", params);
}
public JSONObject facesetGetInfo() throws FaceppParseException {
return request("faceset", "get_info");
}
public JSONObject facesetGetInfo(PostParameters params) throws FaceppParseException{
return request("faceset", "get_info", params);
}
public JSONObject groupCreate() throws FaceppParseException {
return request("group", "create");
}
public JSONObject groupCreate(PostParameters params) throws FaceppParseException{
return request("group", "create", params);
}
public JSONObject groupDelete() throws FaceppParseException {
return request("group", "delete");
}
public JSONObject groupDelete(PostParameters params) throws FaceppParseException{
return request("group", "delete", params);
}
public JSONObject groupAddPerson() throws FaceppParseException {
return request("group", "add_person");
}
public JSONObject groupAddPerson(PostParameters params) throws FaceppParseException{
return request("group", "add_person", params);
}
public JSONObject groupRemovePerson() throws FaceppParseException {
return request("group", "remove_person");
}
public JSONObject groupRemovePerson(PostParameters params) throws FaceppParseException{
return request("group", "remove_person", params);
}
public JSONObject groupSetInfo() throws FaceppParseException {
return request("group", "set_info");
}
public JSONObject groupSetInfo(PostParameters params) throws FaceppParseException{
return request("group", "set_info", params);
}
public JSONObject groupGetInfo() throws FaceppParseException {
return request("group", "get_info");
}
public JSONObject groupGetInfo(PostParameters params) throws FaceppParseException{
return request("group", "get_info", params);
}
public JSONObject infoGetImage() throws FaceppParseException {
return request("info", "get_image");
}
public JSONObject infoGetImage(PostParameters params) throws FaceppParseException{
return request("info", "get_image", params);
}
public JSONObject infoGetFace() throws FaceppParseException {
return request("info", "get_face");
}
public JSONObject infoGetFace(PostParameters params) throws FaceppParseException{
return request("info", "get_face", params);
}
public JSONObject infoGetPersonList() throws FaceppParseException {
return request("info", "get_person_list");
}
public JSONObject infoGetPersonList(PostParameters params) throws FaceppParseException{
return request("info", "get_person_list", params);
}
public JSONObject infoGetFacesetList() throws FaceppParseException {
return request("info", "get_faceset_list");
}
public JSONObject infoGetFacesetList(PostParameters params) throws FaceppParseException{
return request("info", "get_faceset_list", params);
}
public JSONObject infoGetGroupList() throws FaceppParseException {
return request("info", "get_group_list");
}
public JSONObject infoGetGroupList(PostParameters params) throws FaceppParseException{
return request("info", "get_group_list", params);
}
public JSONObject infoGetSession() throws FaceppParseException {
return request("info", "get_session");
}
public JSONObject infoGetSession(PostParameters params) throws FaceppParseException{
return request("info", "get_session", params);
}
/**
* @deprecated this api is deprecated
* @return
* @throws FaceppParseException
*/
public JSONObject infoGetQuota() throws FaceppParseException {
return request("info", "get_quota");
}
/**
* @deprecated this api is deprecated
* @return
* @throws FaceppParseException
*/
public JSONObject infoGetQuota(PostParameters params) throws FaceppParseException{
return request("info", "get_quota", params);
}
public JSONObject infoGetApp() throws FaceppParseException {
return request("info", "get_app");
}
public JSONObject infoGetApp(PostParameters params) throws FaceppParseException{
return request("info", "get_app", params);
}
}
================================================
FILE: sdk_unuse/MainActivity.java
================================================
package mrfu.face.sdk;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.provider.MediaStore.Images.ImageColumns;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.facepp.error.FaceppParseException;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import mrfu.face.R;
/**
* A simple demo, get a picture form your phone
* Use the facepp api to detect
* Find all face on the picture, and mark them out.
* @author moon5ckq
*/
public class MainActivity extends Activity {
final private static String TAG = "MainActivity";
final private int PICTURE_CHOOSE = 1;
private ImageView imageView = null;
private Bitmap img = null;
private Button buttonDetect = null;
private TextView textView = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Button button = (Button)this.findViewById(R.id.button1);
// button.setOnClickListener(new OnClickListener() {
//
// public void onClick(View arg0) {
// //get a picture form your phone
// Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
// photoPickerIntent.setType("image/*");
// startActivityForResult(photoPickerIntent, PICTURE_CHOOSE);
// }
// });
// textView = (TextView)this.findViewById(R.id.textView1);
// buttonDetect = (Button)this.findViewById(R.id.button2);
buttonDetect.setVisibility(View.INVISIBLE);
buttonDetect.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
textView.setText("Waiting ...");
FaceppDetect faceppDetect = new FaceppDetect();
faceppDetect.setDetectCallback(new DetectCallback() {
public void detectResult(JSONObject rst) {
//Log.v(TAG, rst.toString());
//use the red paint
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(Math.max(img.getWidth(), img.getHeight()) / 100f);
//create a new canvas
Bitmap bitmap = Bitmap.createBitmap(img.getWidth(), img.getHeight(), img.getConfig());
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(img, new Matrix(), null);
try {
//find out all faces
final int count = rst.getJSONArray("face").length();
for (int i = 0; i < count; ++i) {
float x, y, w, h;
//get the center point
x = (float)rst.getJSONArray("face").getJSONObject(i)
.getJSONObject("position").getJSONObject("center").getDouble("x");
y = (float)rst.getJSONArray("face").getJSONObject(i)
.getJSONObject("position").getJSONObject("center").getDouble("y");
//get face size
w = (float)rst.getJSONArray("face").getJSONObject(i)
.getJSONObject("position").getDouble("width");
h = (float)rst.getJSONArray("face").getJSONObject(i)
.getJSONObject("position").getDouble("height");
//change percent value to the real size
x = x / 100 * img.getWidth();
w = w / 100 * img.getWidth() * 0.7f;
y = y / 100 * img.getHeight();
h = h / 100 * img.getHeight() * 0.7f;
//draw the box to mark it out
canvas.drawLine(x - w, y - h, x - w, y + h, paint);
canvas.drawLine(x - w, y - h, x + w, y - h, paint);
canvas.drawLine(x + w, y + h, x - w, y + h, paint);
canvas.drawLine(x + w, y + h, x + w, y - h, paint);
}
//save new image
img = bitmap;
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
//show the image
imageView.setImageBitmap(img);
textView.setText("Finished, "+ count + " faces.");
}
});
} catch (JSONException e) {
e.printStackTrace();
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
textView.setText("Error.");
}
});
}
}
});
faceppDetect.detect(img);
}
});
// imageView = (ImageView)this.findViewById(R.id.imageView1);
imageView.setImageBitmap(img);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
//the image picker callback
if (requestCode == PICTURE_CHOOSE) {
if (intent != null) {
//The Android api ~~~
//Log.d(TAG, "idButSelPic Photopicker: " + intent.getDataString());
Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null);
cursor.moveToFirst();
int idx = cursor.getColumnIndex(ImageColumns.DATA);
String fileSrc = cursor.getString(idx);
//Log.d(TAG, "Picture:" + fileSrc);
//just read size
Options options = new Options();
options.inJustDecodeBounds = true;
img = BitmapFactory.decodeFile(fileSrc, options);
//scale size to read
options.inSampleSize = Math.max(1, (int)Math.ceil(Math.max((double)options.outWidth / 1024f, (double)options.outHeight / 1024f)));
options.inJustDecodeBounds = false;
img = BitmapFactory.decodeFile(fileSrc, options);
textView.setText("Clik Detect. ==>");
imageView.setImageBitmap(img);
buttonDetect.setVisibility(View.VISIBLE);
}
else {
Log.d(TAG, "idButSelPic Photopicker canceled");
}
}
}
private class FaceppDetect {
DetectCallback callback = null;
public void setDetectCallback(DetectCallback detectCallback) {
callback = detectCallback;
}
public void detect(final Bitmap image) {
new Thread(new Runnable() {
public void run() {
HttpRequests httpRequests = new HttpRequests("4480afa9b8b364e30ba03819f3e9eff5", "Pz9VFT8AP3g_Pz8_dz84cRY_bz8_Pz8M", true, false);
//Log.v(TAG, "image size : " + img.getWidth() + " " + img.getHeight());
ByteArrayOutputStream stream = new ByteArrayOutputStream();
float scale = Math.min(1, Math.min(600f / img.getWidth(), 600f / img.getHeight()));
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
Bitmap imgSmall = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, false);
//Log.v(TAG, "imgSmall size : " + imgSmall.getWidth() + " " + imgSmall.getHeight());
imgSmall.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] array = stream.toByteArray();
try {
//detect
JSONObject result = httpRequests.detectionDetect(new PostParameters().setImg(array));
//finished , then call the callback function
if (callback != null) {
callback.detectResult(result);
}
} catch (FaceppParseException e) {
e.printStackTrace();
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
textView.setText("Network error.");
}
});
}
}
}).start();
}
}
interface DetectCallback {
void detectResult(JSONObject rst);
}
}
================================================
FILE: sdk_unuse/PostParameters.java
================================================
package mrfu.face.sdk;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Random;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
/**
* Http Multipart
* {@code new PostParameters().setMode("oneface").setImg(new File("...")).setTag("some message")}
* @author moon5kcq
* @since 1.0.0
* @version 1.3.0
*/
public class PostParameters {
private MultipartEntity multiPart = null;
private final static int boundaryLength = 32;
private final static String boundaryAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
private String boundary;
/**
* auto generate boundary string
* @return a boundary string
*/
private String getBoundary() {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < boundaryLength; ++i)
sb.append(boundaryAlphabet.charAt(random.nextInt(boundaryAlphabet.length())));
return sb.toString();
}
/**
* @return get MultipartEntity (apache)
*/
public MultipartEntity getMultiPart() {
return multiPart;
}
/**
* default boundary is auto generate {@link #getBoundary()}
*/
public PostParameters() {
super();
boundary = getBoundary();
multiPart = new MultipartEntity(HttpMultipartMode.STRICT , boundary, Charset.forName("UTF-8"));
}
/**
* @return multipart boundary string
*/
public String boundaryString() {
return boundary;
}
/**
* async=true|false
* @param flag
* @return this
*/
public PostParameters setAsync(boolean flag) {
addString("async", ""+flag);
return this;
}
/**
* url=...
* @param url
* @return this
*/
public PostParameters setUrl(String url){
addString("url", url);
return this;
}
/**
* attribute = gender | age | race | all | none
* @param type
* @return this
*/
public PostParameters setAttribute(String type){
addString("attribute", type);
return this;
}
/**
* tag=...
* @param tag
* @return this
*/
public PostParameters setTag(String tag){
addString("tag", tag);
return this;
}
/**
* img=...
* @param file
* @return this
*/
public PostParameters setImg(File file) {
multiPart.addPart("img", new FileBody(file));
return this;
}
/**
* img=...
* @param data
* @return this
*/
public PostParameters setImg(byte[] data) {
setImg(data, "NoName");
return this;
}
/**
* img=...(name in multipart is ...)
* @param data
* @param fileName
* @return this
*/
public PostParameters setImg(byte[] data, String fileName) {
multiPart.addPart("img", new ByteArrayBody(data, fileName));
return this;
}
/**
* face_id1=...
* @param id
* @return this
*/
public PostParameters setFaceId1(String id){
addString("face_id1", id);
return this;
}
/**
* face_id2=...
* @param id
* @return this
*/
public PostParameters setFaceId2(String id){
addString("face_id2", id);
return this;
}
/**
* group_name=...
* @param groupName
* @return this
*/
public PostParameters setGroupName(String groupName){
addString("group_name", groupName);
return this;
}
/**
* group_id=...
* @param groupId
* @return this
*/
public PostParameters setGroupId(String groupId){
addString("group_id", groupId);
return this;
}
/**
* key_face_id=...
* @param id
* @return this
*/
public PostParameters setKeyFaceId(String id){
addString("key_face_id", id);
return this;
}
/**
* count=...
* @param count
* @return this
*/
public PostParameters setCount(int count) {
addString("count", new Integer(count).toString());
return this;
}
/**
* type= all | search | recognize
* @param type
* @return this
*/
public PostParameters setType(String type) {
addString("type", type);
return this;
}
/**
* face_id=...
* @param faceId
* @return this
*/
public PostParameters setFaceId(String faceId) {
addString("face_id", faceId);
return this;
}
/**
* faceset_id=...
* @param facesetId
* @return this
*/
public PostParameters setFacesetId(String facesetId) {
addString("faceset_id", facesetId);
return this;
}
/**
* faceset_id=..., ..., ...
* @param facesetIds
* @return this
*/
public PostParameters setFacesetId(String[] facesetId) {
setFacesetId(toStringList(facesetId));
return this;
}
/**
* faceset_id=...
* @param facesetIds
* @return this
*/
public PostParameters setFacesetId(ArrayList facesetId) {
setFacesetId(toStringList(facesetId));
return this;
}
/**
* person_id=...
* @param personId
* @return this
*/
public PostParameters setPersonId(String personId) {
addString("person_id", personId);
return this;
}
/**
* person_name=...
* @param personName
* @return this
*/
public PostParameters setPersonName(String personName) {
addString("person_name", personName);
return this;
}
/**
* name=...
* @param name
* @return this
*/
public PostParameters setName(String name) {
addString("name", name);
return this;
}
/**
* session_id=...
* @param id
* @return this
*/
public PostParameters setSessionId(String id) {
addString("session_id", id);
return this;
}
/**
* mode= oneface | normal
* @param type
* @return this
*/
public PostParameters setMode(String type) {
addString("mode", type);
return this;
}
/**
* face_id=... , ... , ...
* @param faceIds
* @return this
*/
public PostParameters setFaceId(String[] faceIds) {
return setFaceId(toStringList(faceIds));
}
/**
* person_id=... , ... , ...
* @param personIds
* @return this
*/
public PostParameters setPersonId(String[] personIds) {
return setPersonId(toStringList(personIds));
}
/**
* person_name=... , ... , ...
* @param personNames
* @return this
*/
public PostParameters setPersonName(String[] personNames) {
return setPersonName(toStringList(personNames));
}
/**
* group_id=... , ... , ...
* @param groupIds
* @return this
*/
public PostParameters setGroupId(String[] groupIds) {
return setGroupId(toStringList(groupIds));
}
/**
* group_name=... , ... , ...
* @param groupNames
* @return this
*/
public PostParameters setGroupName(String[] groupNames) {
return setGroupName(toStringList(groupNames));
}
/**
* face=... , ... , ...
* @param faceIds
* @return this
*/
public PostParameters setFaceId(ArrayList faceIds) {
return setFaceId(toStringList(faceIds));
}
/**
* person_id=... , ... , ...
* @param personIds
* @return this
*/
public PostParameters setPersonId(ArrayList personIds) {
return setPersonId(toStringList(personIds));
}
/**
* person_name=... , ... , ...
* @param personNames
* @return this
*/
public PostParameters setPersonName(ArrayList personNames) {
return setPersonName(toStringList(personNames));
}
/**
* group_id=... , ... , ...
* @param groupIds
* @return this
*/
public PostParameters setGroupId(ArrayList groupIds) {
return setGroupId(toStringList(groupIds));
}
/**
* group_name=... , ... , ...
* @param groupNames
* @return this
*/
public PostParameters setGroupName(ArrayList groupNames) {
return setGroupName(toStringList(groupNames));
}
/**
* img_id=...
* @param imgId
* @return this
*/
public PostParameters setImgId(String imgId) {
addString("img_id", imgId);
return this;
}
/**
* faceset_name=...
* @param facesetName
* @return this
*/
public PostParameters setFacesetName(String facesetName) {
addString("faceset_name", facesetName);
return this;
}
/**
* faceset_name=... , ... , ...
* @param facesetNames
* @return this
*/
public PostParameters setFacesetName(ArrayList facesetNames) {
return setFacesetName(toStringList(facesetNames));
}
/**
* faceset_name=... , ... , ...
* @param facesetNames
* @return this
*/
public PostParameters setFacesetName(String[] facesetNames) {
return setFacesetName(toStringList(facesetNames));
}
/**
* `attr`=`value`
* @param attr value
* @return this
*/
public PostParameters addAttribute(String attr, String value) {
addString(attr, value);
return this;
}
private void addString(String id, String str) {
try {
multiPart.addPart(id, new StringBody(str, Charset.forName("UTF-8")));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private String toStringList(String[] sa) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < sa.length; ++i) {
if (i != 0) sb.append(',');
sb.append(sa[i]);
}
return sb.toString();
}
private String toStringList(ArrayList sa) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < sa.size(); ++i) {
if (i != 0) sb.append(',');
sb.append(sa.get(i));
}
return sb.toString();
}
}
================================================
FILE: settings.gradle
================================================
include ':app'