Android 腾讯优图开发问题总结
马上要提交腾讯优图的第一次比赛的作品了,才匆匆忙忙开始这次项目,期间遇到不少问题,发现网上并没有相关解答,毕竟优图API比较新吧,用的人比较少。我就把我遇到的问题总结一下吧。
接入优图检测人脸失败,错误码SDK_IMAGE_FACEDETECT_FAILED = -1101
具体表现
下载优图Android SDK,里面获取优图官方测试图片文件,检测返回数据正确,使用自己的自拍返回失败(刚开始还以为是自己长得丑检测不出来 TAT)
注:下载优图Android SDK不是类似微信SDK一样给出Jar包和文档,而是直接将接口调用封装到Youtu.java文件中,测试项目用Android Studio创建,这里开发者按照自己的包结构将相关的源文件导入到Eclipse工程中。
提供的getBitmap()
的问题
以下是下载SDK中MainActivity.java中提供的getBitmap()方法:
private Bitmap getBitmap(String path , int maxWidth, int maxHeight){
//先解析图片边框的大小
BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = true;
ops.inSampleSize = 1;
int oHeight = ops.outHeight;
int oWidth = ops.outWidth;
//控制压缩比
int contentHeight = maxWidth;
int contentWidth = maxHeight;
if(((float)oHeight/contentHeight) < ((float)oWidth/contentWidth)){
ops.inSampleSize = (int) Math.ceil((float)oWidth/contentWidth);
}else{
ops.inSampleSize = (int) Math.ceil((float)oHeight/contentHeight);
}
ops.inJustDecodeBounds = false;
Bitmap bm = BitmapFactory.decodeFile(path, ops);
return bm;
}
这个方法事实上是控制获取Bitmap的宽高,应该是API的要求,不过经过测试后,并不能很好的进行压缩,比如我的红米2A
测试机自拍得到的Bitmap是1200 * 1600,调用返回结果仍然是1200 * 1600。这里我们对上述方法进行修改,添加语句:
ops.inJustDecodeBounds = true;// 防止Out Of Memory错误
Bitmap bm = BitmapFactory.decodeFile(path, ops);
获取图片的基本数据。经过测试图片能够得到正确压缩。然而…
Bitmap旋转的问题
优图API正向识别
接上然而…然而仍然返回错误码SDK_IMAGE_FACEDETECT_FAILED = -1101,这回真的是我长得丑没差了吧。。。然而用腾讯优图测试图片中葛大爷的图片居然都能检测出来。。。TAT
言归正传,只有继续调试:
这一次,我把需要检测的Bitmap对象写入SD卡根目录,终于发现问题关键:
腾讯优图的测试图片打开后人像都是正方向,而我用红米2A自拍来的图片是正时针旋转270度的治好劲椎病模式的图片。那么将Bitmap对象旋转-90度或者正270度试试?于是继续修改getBitmap
方法:
ops.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, ops);
if (bm != null){
Matrix m = new Matrix();
m.setRotate(-90, (float) bm.getWidth() / 2,(float) bm.getHeight() / 2);
Bitmap bm1 = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),bm.getHeight(), m, true);
return bm1;//旋转后的图片
}
Android机型兼容性考虑
我的红米2A测试机终于可以正确识别了,网络良好的情况下识别效率和识别的准确度还挺高的,但是…换了同学的MX4之后就又不行了。按照上述方法接着存图片,发现人家MX4自拍得来照片就是正向的,根本不需要旋转,如何解决这个问题呢?参考:http://blog.csdn.net/zdw890412/article/details/7360354
我们首先获取旋转角度,根据不同的角度进行相应的旋转,如果旋转角度为0,则不旋转,这样,再次修改getBitmap()
方法,加入判断部分:
Bitmap bm = null;
try {
ExifInterface exifInterface = new ExifInterface(path);
int result = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
int rotate = 0;
switch (result) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
default:
break;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
//...
//压缩代码略
//...
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
if (rotate > 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotate, (float) options.outWidth / 2,
(float) options.outHeight / 2);
Bitmap rotateBm = Bitmap.createBitmap(bm, 0, 0,
options.outWidth, options.outHeight, matrix, true);
if(rotateBm !=null){
bm.recycle();
bm=rotateBm;
}
}
return bm;
至此基本上大功告成。在HTC one 801e 手机上测试,发现根本获取不了Bitmap对象,返回空指针错误。
Android系统兼容性考虑
测试了很多机型,红米2A,三星Note3,魅族MX4,都是没有问题的,唯独用到HTC one时就崩溃了,返回空指针错误。HTC还真是傲娇。。
怀疑是系统没有找到图片文件,我们知道调用系统相册将返回Uri数据,我们获取图片需要图片的路径,在腾讯优图的测试源码中, 提供了uri2Path(Uri uri)
方法,用来获取图片路径,通过打印发现HTC one 801e返回的image/jpeg
这个是Uri中的一个字段,但是到了Android的高版本以后,Uri的结构发生了一些变化,所以用老方法获取的Uri的位置得到的字段向下兼容,到了高版本就不行了,参考:http://blog.csdn.net/wblyuyang/article/details/45223813
重写uri2Path(Uri uri)
方法:
@SuppressLint("NewApi")
public String uri2Path(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/"
+ split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] { split[1] };
return getDataColumn(context, contentUri, selection,
selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri
.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri
.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri
.getAuthority());
}
OK,这次可以开心的听听音乐,谢谢博客了。在我所拥有的的测试条件下(红米2A,魅族MX4,三星Note3,HTC one 801e),皆可以完成腾讯优图API的正确接入。
总结
由于这次使用腾讯优图的API接入开发,不仅仅考虑自己的测试,还需要考虑APP的兼容性问题,主要考虑以下几点:
不同机型自拍保存图片方向(与不同厂商摄像头调教有关)
高低版本Android系统的兼容性问题
不得不说,现在Android开发很多东西都特别混乱,增加了开发难度。