运行效果图
如果你对这个效果图不满意就不用往下看了,那样只会浪费你的时间。
一、创建平台应用
先登录百度开放平台
然后进入管理控制台找到图像识别
点击进入。
创建应用
然后点击下方的立即创建按钮。
注意看下图标注的信息
由于图像识别没有直接的Android SDK,因此本文将通过API访问进行图像的识别。
二、创建Android项目
通过API方式,则需要先完成鉴权认证,然后拿到Access Token,通过这个Access Token才能去请求这个图像识别的接口,这里要分为两步走。现在思路清楚了,下面先创建一个项目吧,命名为ImageDiscernDemo。
项目创建好之后,先配置项目。打开工程的build.gradle,添加如下代码:
代码语言:txt复制 maven { url "https://jitpack.io" }
添加位置如下:
然后是修改app下的build.gradle,有两处
代码语言:txt复制 compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
代码语言:txt复制 //权限请求框架
implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation "io.reactivex.rxjava2:rxjava:2.0.0"
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.4.1'
//RecyclerView最好的适配器,让你的适配器一目了然,告别代码冗余
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
//Glide框架
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
//添加material库
implementation 'com.google.android.material:material:1.2.1'
添加位置如下:
然后Sync Now,完成后来配置AndroidManifest.xml,添加如下权限:
代码语言:txt复制 <!--网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--文件读写-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--相机-->
<uses-permission android:name="android.permission.CAMERA"/>
再来配置一个FileProvider,在res文件夹下新建一个xml文件夹,与layout文件夹同级,在xml文件夹下新建一个file_paths.xml,里面的代码如下:
代码语言:txt复制<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="" />
</paths>
然后在AndroidManifest.xml中配置
代码语言:txt复制 <provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.llw.imagediscerndemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
添加位置如下图:
下面我们首先简单来写一个网络访问的工具类。
三、网络访问框架
在com.llw.imagediscerndemo下新建一个network包,包下新建一个ServiceGenerator类,里面的代码如下:
代码语言:txt复制package com.llw.imagediscerndemo.network;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* 接口地址管理
*
* @author llw
*/
public class ServiceGenerator {
/**
* 默认地址
*/
public static String BASE_URL = "https://aip.baidubce.com";
/**
* 创建服务 参数就是API服务
*
* @param serviceClass 服务接口
* @param <T> 泛型规范
* @return api接口服务
*/
public static <T> T createService(Class<T> serviceClass) {
//创建OkHttpClient构建器对象
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
//设置请求超时的时间,这里是10秒
okHttpClientBuilder.connectTimeout(20000, TimeUnit.MILLISECONDS);
//消息拦截器 因为有时候接口不同在排错的时候 需要先从接口的响应中做分析。利用了消息拦截器可以清楚的看到接口返回的所有内容
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
//setlevel用来设置日志打印的级别,共包括了四个级别:NONE,BASIC,HEADER,BODY
//BASEIC:请求/响应行
//HEADER:请求/响应行 头
//BODY:请求/响应航 头 体
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//为OkHttp添加消息拦截器
okHttpClientBuilder.addInterceptor(httpLoggingInterceptor);
//在Retrofit中设置httpclient
//设置地址 就是上面的固定地址,如果你是本地访问的话,可以拼接上端口号 例如 ":8080"
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
//用Gson把服务端返回的json数据解析成实体
.addConverterFactory(GsonConverterFactory.create())
//放入OKHttp,之前说过retrofit是对OkHttp的进一步封装
.client(okHttpClientBuilder.build())
.build();
//返回这个创建好的API服务
return retrofit.create(serviceClass);
}
}
很简单的代码,也都是网络上常见的,OkHttp Retrofit。
这里面的默认地址 https://aip.baidubce.com是图像识别API的固定地址,后面的有变化的,通过接口来配置。
然后再写一个NetCallBack类,用来处理Retrofit的返回,里面的代码如下:
代码语言:txt复制package com.llw.imagediscerndemo.network;
import android.util.Log;
import com.google.gson.Gson;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* 网络请求回调
*
* @param <T>
*/
public abstract class NetCallBack<T> implements Callback<T> {//这里实现了retrofit2.Callback
//访问成功回调
@Override
public void onResponse(Call<T> call, Response<T> response) {//数据返回
if (response != null && response.body() != null && response.isSuccessful()) {
onSuccess(call, response);
} else {
onFailed(response.raw().toString());
}
}
//访问失败回调
@Override
public void onFailure(Call<T> call, Throwable t) {
Log.d("data str", t.toString());
onFailed(t.toString());
}
//数据返回
public abstract void onSuccess(Call<T> call, Response<T> response);
//失败异常
public abstract void onFailed(String errorStr);
}
那么这样简单的网络框架写好了。
四、添加请求API接口
百度的图像识别,首先要完成鉴权认证,拿到一个Token,然后通过这个Token再去请求图像识别的API接口才行,所以要完成两步操作。先来看第一步
鉴权的地址如下:
代码语言:txt复制https://aip.baidubce.com/oauth/2.0/token
这里面需要再带三个Url参数,使用Post请求方式。
代码语言:txt复制grant_type: 必须参数,固定为client_credentials;
client_id: 必须参数,应用的API Key;
client_secret: 必须参数,应用的Secret Key;
返回的是一串JSON字符串,如下:
代码语言:txt复制{
"refresh_token": "25.b55fe1d287227ca97aab219bb249b8ab.315360000.1798284651.282335-8574074",
"expires_in": 2592000,
"scope": "public wise_adapt",
"session_key": "9mzdDZXu3dENdFZQurfg0Vz8slgSgvvOAUebNFzyzcpQ5EnbxbF hfG9DQkpUVQdh4p6HbQcAiz5RmuBAja1JJGgIdJI",
"access_token": "24.6c5e1ff107f0e8bcef8c46d3424a0e78.2592000.1485516651.282335-8574074",
"session_secret": "dfac94a3489fe9fca7c3221cbf7525ff"
}
当请求和返回都确定之后,我们就可以构建请求实体和返回实体了。
而请求实体都是放在Url中的,因此不需要通过实体来构建,直接传参数就好。
在com.llw.imagediscerndemo包下新建一个model包,包下新建一个GetTokenResponse类,里面的代码如下:
代码语言:txt复制package com.llw.imagediscerndemo.model;
/**
* 获取鉴权认证Token响应实体
*/
public class GetTokenResponse {
/**
* refresh_token : 25.b55fe1d287227ca97aab219bb249b8ab.315360000.1798284651.282335-8574074
* expires_in : 2592000
* scope : public wise_adapt
* session_key : 9mzdDZXu3dENdFZQurfg0Vz8slgSgvvOAUebNFzyzcpQ5EnbxbF hfG9DQkpUVQdh4p6HbQcAiz5RmuBAja1JJGgIdJI
* access_token : 24.6c5e1ff107f0e8bcef8c46d3424a0e78.2592000.1485516651.282335-8574074
* session_secret : dfac94a3489fe9fca7c3221cbf7525ff
*/
private String refresh_token;
private long expires_in;
private String scope;
private String session_key;
private String access_token;
private String session_secret;
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expires_in) {
this.expires_in = expires_in;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getSession_key() {
return session_key;
}
public void setSession_key(String session_key) {
this.session_key = session_key;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getSession_secret() {
return session_secret;
}
public void setSession_secret(String session_secret) {
this.session_secret = session_secret;
}
}
这是通过刚才的返回数据生成的实体Bean,当网络请求返回数据后通过Retrofit会解析成这个返回实体。
下面添加接口,在network包下新建一个ApiService接口,里面的代码如下:
代码语言:txt复制package com.llw.imagediscerndemo.network;
import com.llw.imagediscerndemo.model.GetTokenResponse;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Headers;
import retrofit2.http.POST;
/**
* API服务
*
* @author llw
* @date 2021/4/1 17:48
*/
public interface ApiService {
/**
* 获取鉴权认证Token
* @param grant_type 类型
* @param client_id API Key
* @param client_secret Secret Key
* @return GetTokenResponse
*/
@FormUrlEncoded
@POST("/oauth/2.0/token")
Call<GetTokenResponse> getToken(@Field("grant_type") String grant_type,
@Field("client_id") String client_id,
@Field("client_secret") String client_secret);
}
这里还有一个接口呢,那就是图像识别接口。地址为:
代码语言:txt复制https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general
找个接口比较特殊,官方文档的描述如下:
看你是否理解了。返回数据如下:
代码语言:txt复制{
"log_id": 327863200205075661,
"result_num": 5,
"result": [{
"score": 0.967622,
"root": "公众人物",
"baike_info": {
"baike_url": "http://baike.baidu.com/item/新垣结衣/8035884",
"image_url": "http://imgsrc.baidu.com/baikehttps://img.yuanmabao.com/zijie/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg",
"description": "新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。"
},
"keyword": "新垣结衣"
},
{
"score": 0.716067,
"root": "人物-人物特写",
"keyword": "头发"
},
{
"score": 0.421281,
"root": "商品-穿戴",
"keyword": "围巾"
},
{
"score": 0.22347,
"root": "商品-五金",
"keyword": "拉链"
},
{
"score": 0.028031,
"root": "商品-穿戴",
"keyword": "脖套"
}]
}
通过这个返回示例数据,可以生成一个实体Bean。在model包下新建一个GetDiscernResultResponse类,代码如下:
代码语言:txt复制package com.llw.imagediscerndemo.model;
import java.util.List;
/**
* 获取识别结果响应实体
*/
public class GetDiscernResultResponse {
/**
* log_id : 327863200205075661
* result_num : 5
* result : [{"score":0.967622,"root":"公众人物","baike_info":{"baike_url":"http://baike.baidu.com/item/新垣结衣/8035884","image_url":"http://imgsrc.baidu.com/baikehttps://img.yuanmabao.com/zijie/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg","description":"新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。"},"keyword":"新垣结衣"},{"score":0.716067,"root":"人物-人物特写","keyword":"头发"},{"score":0.421281,"root":"商品-穿戴","keyword":"围巾"},{"score":0.22347,"root":"商品-五金","keyword":"拉链"},{"score":0.028031,"root":"商品-穿戴","keyword":"脖套"}]
*/
private long log_id;
private int result_num;
private List<ResultBean> result;
public long getLog_id() {
return log_id;
}
public void setLog_id(long log_id) {
this.log_id = log_id;
}
public int getResult_num() {
return result_num;
}
public void setResult_num(int result_num) {
this.result_num = result_num;
}
public List<ResultBean> getResult() {
return result;
}
public void setResult(List<ResultBean> result) {
this.result = result;
}
public static class ResultBean {
/**
* score : 0.967622
* root : 公众人物
* baike_info : {"baike_url":"http://baike.baidu.com/item/新垣结衣/8035884","image_url":"http://imgsrc.baidu.com/baikehttps://img.yuanmabao.com/zijie/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg","description":"新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。"}
* keyword : 新垣结衣
*/
private double score;
private String root;
private BaikeInfoBean baike_info;
private String keyword;
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
public BaikeInfoBean getBaike_info() {
return baike_info;
}
public void setBaike_info(BaikeInfoBean baike_info) {
this.baike_info = baike_info;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public static class BaikeInfoBean {
/**
* baike_url : http://baike.baidu.com/item/新垣结衣/8035884
* image_url : http://imgsrc.baidu.com/baikehttps://img.yuanmabao.com/zijie/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg
* description : 新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。
*/
private String baike_url;
private String image_url;
private String description;
public String getBaike_url() {
return baike_url;
}
public void setBaike_url(String baike_url) {
this.baike_url = baike_url;
}
public String getImage_url() {
return image_url;
}
public void setImage_url(String image_url) {
this.image_url = image_url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
}
}
下面在ApiService中添加接口。
代码语言:txt复制 /**
* 获取图像识别结果
* @param accessToken 获取鉴权认证Token
* @param url 网络图片Url
* @return JsonObject
*/
@FormUrlEncoded
@POST("/rest/2.0/image-classify/v2/advanced_general")
@Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Call<GetDiscernResultResponse> getDiscernResult(@Field("access_token") String accessToken,
@Field("url") String url);
我这个写法和官方说明好像有点不同,我没有用Body。选择直接把数据放在Url中请求。
现在万事具备了,下面先来获取鉴权认证Token。