Android 百度图像识别(详细步骤+源码)(1)

在com.llw.imagediscerndemo包下新建一个model包,包下新建一个GetTokenResponse类,里面的代码如下:

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接口,里面的代码如下:

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 getToken(@Field(“grant_type”) String grant_type,

@Field(“client_id”) String client_id,

@Field(“client_secret”) String client_secret);

}

这里还有一个接口呢,那就是图像识别接口。地址为:

https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general

找个接口比较特殊,官方文档的描述如下:

在这里插入图片描述

看你是否理解了。返回数据如下:

{

“log_id”: 327863200205075661,

“result_num”: 5,

“result”: [{

“score”: 0.967622,

“root”: “公众人物”,

“baike_info”: {

“baike_url”: “http://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884”,

“image_url”: “http://imgsrc.baidu.com/baike/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类,代码如下:

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/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884”,“image_url”:“http://imgsrc.baidu.com/baike/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 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 getResult() {

return result;

}

public void setResult(List result) {

this.result = result;

}

public static class ResultBean {

/**

  • score : 0.967622

  • root : 公众人物

  • baike_info : {“baike_url”:“http://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884”,“image_url”:“http://imgsrc.baidu.com/baike/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/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884

  • image_url : http://imgsrc.baidu.com/baike/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中添加接口。

/**

  • 获取图像识别结果

  • @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 getDiscernResult(@Field(“access_token”) String accessToken,

@Field(“url”) String url);

我这个写法和官方说明好像有点不同,我没有用Body。选择直接把数据放在Url中请求。

现在万事具备了,下面先来获取鉴权认证Token。

五、获取鉴权认证Token


打开MainActivity,添加如下代码:

private static final String TAG = “MainActivity”;

/**

  • Api服务

*/

private ApiService service;

/**

  • 鉴权Toeken

*/

private String accessToken;

然后在onCreate方法中对ApiService进行实例化。

service = ServiceGenerator.createService(ApiService.class);

新增如下方法获取Token,

/**

  • 访问API获取接口

*/

private void requestApiGetToken() {

String grantType = “client_credentials”;

String apiKey = “TjPChftoEyBq7Nzm65KNerqr”;

String apiSecret = “eTph4jO95te6R3G2aecktGMbkieOv7rS”;

service.getToken(grantType, apiKey, apiSecret)

.enqueue(new NetCallBack() {

@Override

public void onSuccess(Call call, Response response) {

if (response.body() != null) {

//鉴权Token

accessToken = response.body().getAccess_token();

Log.d(TAG,accessToken);

}

}

@Override

public void onFailed(String errorStr) {

Log.e(TAG, “获取Token失败,失败原因:” + errorStr);

accessToken = null;

}

});

}

然后在onCreate中调用它。

在这里插入图片描述

运行一下,看一下控制台是否打印了日志。

在这里插入图片描述

你可以看到这个Token还是挺长的。对于这个Token,是有有效期的,基本上是一个月,所以你可以不用每次使用时都重新请求这个接口去获取Token,这里可以用缓存来解决这个问题。

说一下逻辑,当通过接口拿到Token时保存Token、Token获取时间、Token有效时长三个数据到缓存中,每一次使用前进行一次判断,首先是判断有没有Token,其次是判断Token有没有过期。那么按照这个思路我们就可以这么写代码了。

这里为了方便我在com.llw.imagediscerndemo包下新建一个util包,包下新建一个Constant类,里面的代码如下:

package com.llw.imagediscerndemo.util;

/**

  • 全局常量

*/

public class Constant {

/**

  • 鉴权Token

*/

public static final String TOKEN = “accessToken”;

/**

  • 获取Token的时间

*/

public static final String GET_TOKEN_TIME = “getTokenTime”;

/**

  • Token有效期

*/

public static final String TOKEN_VALID_PERIOD = “tokenValidPeriod”;

}

这三个值,我刚才也说明过了。下面写一个缓存的SPUtils工具类,里面的代码如下:

package com.llw.imagediscerndemo.util;

import android.content.Context;

import android.content.SharedPreferences;

/**

  • SharedPreferences工具类

  • @author llw

*/

public class SPUtils {

private static final String NAME = “config”;

public static void putBoolean(String key, boolean value, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

sp.edit().putBoolean(key, value).commit();

}

public static boolean getBoolean(String key, boolean defValue, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

return sp.getBoolean(key, defValue);

}

public static void putString(String key, String value, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

sp.edit().putString(key, value).commit();

}

public static String getString(String key, String defValue, Context context) {

if (context != null) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

return sp.getString(key, defValue);

}

return “”;

}

public static void putInt(String key, int value, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

sp.edit().putInt(key, value).commit();

}

public static int getInt(String key, int defValue, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

return sp.getInt(key, defValue);

}

public static void putLong(String key, long value, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

sp.edit().putLong(key, value).commit();

}

public static long getLong(String key, long defValue, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

return sp.getLong(key, defValue);

}

public static void remove(String key, Context context) {

SharedPreferences sp = context.getSharedPreferences(NAME,

Context.MODE_PRIVATE);

sp.edit().remove(key).commit();

}

}

也是很简单的代码,相信你一眼就看明白了,下面就该在MainActivity中去处理缓存数据的存取了。

首先是放缓存,这当然是在请求接口的成功数据返回中放,修改onSuccess中的代码,如下。

@Override

public void onSuccess(Call call, Response response) {

if (response.body() != null) {

//鉴权Token

accessToken = response.body().getAccess_token();

//过期时间 秒

long expiresIn = response.body().getExpires_in();

//当前时间 秒

long currentTimeMillis = System.currentTimeMillis() / 1000;

//放入缓存

SPUtils.putString(Constant.TOKEN, accessToken, MainActivity.this);

SPUtils.putLong(Constant.GET_TOKEN_TIME, currentTimeMillis, MainActivity.this);

SPUtils.putLong(Constant.TOKEN_VALID_PERIOD, expiresIn, MainActivity.this);

}

}

然后写一个判断Token是否过期的方法,方法代码如下:

/**

  • Token是否过期

  • @return

*/

private boolean isTokenExpired() {

//获取Token的时间

long getTokenTime = SPUtils.getLong(Constant.GET_TOKEN_TIME, 0, this);

//获取Token的有效时间

long effectiveTime = SPUtils.getLong(Constant.TOKEN_VALID_PERIOD, 0, this);

//获取当前系统时间

long currentTime = System.currentTimeMillis() / 1000;

return (currentTime - getTokenTime) >= effectiveTime;

}

这个方法也是很好理解的,首先取出缓存中的获取Token的时间,然后获取Token的有效时长,再获取当前系统时间,然后通过当前系统时间减去获得Token的时间,得到的值再与Token有效期做比较,如果大于等于有效期则说明Token过期,返回true,否则返回false。

下面再写一个方法,用来获取Token,同时将我们之前写的代码给串起来。

/**

  • 获取鉴权Token

*/

private String getAccessToken() {

String token = SPUtils.getString(Constant.TOKEN, null, this);

if (token == null) {

//访问API获取接口

requestApiGetToken();

} else {

//则判断Token是否过期

if (isTokenExpired()) {

//过期

requestApiGetToken();

} else {

accessToken = token;

}

}

return accessToken;

}

首先获取缓存中的Token,应用第一次进入肯定是没有值的,没有值则返回默认值null,那么token变量此时为null,那么就会通过接口去获取Token,当获取之后存入缓存,再次进入时,就不是null了,那么就会通过isTokenExpired()方法来判断Token是否过期,过期了也是通过网络请求重新拿到Token放入缓存,如果没有过期则直接使用缓存中的Token,最后返回Token。这方法在onCreate中调用

在这里插入图片描述

六、网络图片Url识别


Token拿到以后我们来进行网络图片Url识别。先说一下思路,首先是通过网络图片url和Token去请求接口,然后获得返回值,此时要显示一个加载条,然后通过返回数据渲染列表,当数据显示在列表之后就完成了。

首先找一个网络图片Url,如下:

https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg

这个网络图片是一个水杯的图片,如下所示:

在这里插入图片描述

首先修改布局activity_main.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:gravity=“center”

android:orientation=“vertical”

tools:context=“.MainActivity”>

<ImageView

android:id=“@+id/iv_picture”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:scaleType=“centerCrop” />

<LinearLayout

android:layout_marginBottom=“16dp”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_alignParentBottom=“true”

android:gravity=“center”>

<Button

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:onClick=“IdentifyWebPictures”

android:text=“识别网络图片” />

<ProgressBar

android:visibility=“gone”

android:id=“@+id/pb_loading”

android:layout_centerInParent=“true”

android:layout_width=“60dp”

android:layout_height=“60dp”/>

然后在MainActivity中写入IdentifyWebPictures方法,代码如下:

/**

  • 识别网络图片

  • @param view

*/

public void IdentifyWebPictures(View view) {

}

首先创建对象

/**

  • 显示图片

*/

private ImageView ivPicture;

/**

  • 进度条

*/

private ProgressBar pbLoading;

然后在onCreate中绑定xml中的控件id。

ivPicture = findViewById(R.id.iv_picture);

pbLoading = findViewById(R.id.pb_loading);

下面来修改IdentifyWebPictures()方法的代码,如下:

public void IdentifyWebPictures(View view) {

pbLoading.setVisibility(View.VISIBLE);

if (accessToken == null) {

showMsg(“获取AccessToken到null”);

return;

}

String imgUrl = “https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg”;

//显示图片

Glide.with(this).load(imgUrl).into(ivPicture);

showMsg(“图像识别中”);

service.getDiscernResult(accessToken, imgUrl).enqueue(new NetCallBack() {

@Override

public void onSuccess(Call call, Response response) {

List<GetDiscernResultResponse.ResultBean> result = response.body() != null ? response.body().getResult() : null;

if (result != null && result.size() > 0) {

//显示识别结果

showDiscernResult(result);

} else {

pbLoading.setVisibility(View.GONE);

showMsg(“未获得相应的识别结果”);

}

}

@Override

public void onFailed(String errorStr) {

pbLoading.setVisibility(View.GONE);

Log.e(TAG, “图像识别失败,失败原因:” + errorStr);

}

});

}

当点击按钮时,显示进度条,然后通过getAccessToken()方法获取Token,这里获取Token,有两种方式,通过网络获取和本地缓存获取,之后显示网络图片在ImageView控件中,Toast提示一下,之后请求的成功和失败的回调了,在成功的回调中先判断数据是否为空,不为空再通过showDiscernResult()方法去显示数据,下面写这个方法。

/**

  • 显示识别的结果列表

  • @param result

*/

private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {

}

showMsg方法:

/**

  • Toast提示

  • @param msg 内容

*/

private void showMsg(String msg){

Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();

}

为了不占用屏幕的控件,我这里打算用一个弹窗来显示数据,弹窗里面是一个列表,列表通过item布局构建,数据由刚才的方法传递进来,我们一步一步来写,首先构建item的布局。在layout下新建一个item_result_rv.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“match_parent”

android:padding=“16dp”

android:background=“#FFF”

android:layout_marginBottom=“1dp”

android:layout_height=“wrap_content”>

<TextView

android:id=“@+id/tv_keyword”

android:textSize=“16sp”

android:textColor=“#000”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

<TextView

android:layout_marginTop=“@dimen/dp_4”

android:layout_below=“@+id/tv_keyword”

android:id=“@+id/tv_root”

android:textSize=“14sp”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

<TextView

android:id=“@+id/tv_score”

android:layout_alignParentEnd=“true”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

item布局有了,下面构建弹窗的布局代码,在layout下新建一个 dialog_bottom.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:background=“#EEE”

android:orientation=“vertical”>

<TextView

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:background=“#FFF”

android:gravity=“center”

android:padding=“16dp”

android:text=“识别结果” />

<androidx.recyclerview.widget.RecyclerView

android:id=“@+id/rv_result”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_marginTop=“1dp” />

下面布局都有了,先构建这个列表的适配器,在com.llw.imagediscerndemo下新建一个adapter包,包下新建一个DiscernResultAdapter类,里面的代码如下:

package com.llw.imagediscerndemo.adapter;

import androidx.annotation.Nullable;

import com.chad.library.adapter.base.BaseQuickAdapter;

import com.chad.library.adapter.base.BaseViewHolder;

import com.llw.imagediscerndemo.R;

import com.llw.imagediscerndemo.model.GetDiscernResultResponse;

import java.util.List;

/**

  • 识别结果列表适配器

  • @author llw

*/

public class DiscernResultAdapter extends BaseQuickAdapter<GetDiscernResultResponse.ResultBean, BaseViewHolder> {

public DiscernResultAdapter(int layoutResId, @Nullable List<GetDiscernResultResponse.ResultBean> data) {

super(layoutResId, data);

}

@Override

protected void convert(BaseViewHolder helper, GetDiscernResultResponse.ResultBean item) {

helper.setText(R.id.tv_keyword,item.getKeyword())

.setText(R.id.tv_root,item.getRoot())

.setText(R.id.tv_score,String.valueOf(item.getScore()));

}

}

万事具备,只差显示数据了,下面进入MainActivity中,首先创建对象

/**

  • 底部弹窗

*/

private BottomSheetDialog bottomSheetDialog;

/**

  • 弹窗视图

*/

private View bottomView;

然后在onCreate中实例化,

bottomSheetDialog = new BottomSheetDialog(this);

bottomView = getLayoutInflater().inflate(R.layout.dialog_bottom, null);

然后修改showDiscernResult方法,代码如下:

private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {

bottomSheetDialog.setContentView(bottomView);

bottomSheetDialog.getWindow().findViewById(R.id.design_bottom_sheet).setBackgroundColor(Color.TRANSPARENT);

RecyclerView rvResult = bottomView.findViewById(R.id.rv_result);

DiscernResultAdapter adapter = new DiscernResultAdapter(R.layout.item_result_rv, result);

rvResult.setLayoutManager(new LinearLayoutManager(this));

rvResult.setAdapter(adapter);

//隐藏加载

pbLoading.setVisibility(View.GONE);

//显示弹窗

bottomSheetDialog.show();

}

下面运行一下:

在这里插入图片描述

可以看到结果识别到了。

七、相册图片识别


在实际应用中,更多是采用本地的图片进行识别,通常是选择拍照的图片或者打开相册获取图片,先来看看通过相册获取图片进行图像识别。要实现这个功能首先要改一下接口,加一个image参数。

在这里插入图片描述

然后修改ImageDiscern方法。

/**

  • 图像识别请求

  • @param token token

  • @param imageBase64 图片Base64

  • @param imgUrl 网络图片Url

*/

private void ImageDiscern(String token, String imageBase64, String imgUrl) {

service.getDiscernResult(token, imageBase64, imgUrl).enqueue(new NetCallBack() {

@Override

public void onSuccess(Call call, Response response) {

List<GetDiscernResultResponse.ResultBean> result = response.body() != null ? response.body().getResult() : null;

if (result != null && result.size() > 0) {

//显示识别结果

showDiscernResult(result);

} else {

pbLoading.setVisibility(View.GONE);

showMsg(“未获得相应的识别结果”);

}

}

@Override

public void onFailed(String errorStr) {

pbLoading.setVisibility(View.GONE);

Log.e(TAG, “图像识别失败,失败原因:” + errorStr);

}

});

}

这个方法接收三个参数,Token、ImageBase64、图片Url。ImageBase64和图片Url只能二选一。选其中一个另一个则传null。比如之前的通过网络图片Url识别。

在这里插入图片描述

接口的相关方法都改好了,下面来写打开相册的方法。Android6.0以后读写文件都属于危险权限,因此需要动态请求。在MainActivity中声明:

private RxPermissions rxPermissions;

然后在onCreate中实例化

rxPermissions = new RxPermissions(this);

下面修改布局,在之前的按钮后面再加一个按钮

<Button

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:onClick=“IdentifyAlbumPictures”

android:text=“识别相册图片” />

然后在MainActivity中增加IdentifyAlbumPictures方法,代码如下:

/**

  • 识别相册图片

  • @param view

*/

@SuppressLint(“CheckResult”)

public void IdentifyAlbumPictures(View view) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

rxPermissions.request(

Manifest.permission.READ_EXTERNAL_STORAGE,

Manifest.permission.WRITE_EXTERNAL_STORAGE)

.subscribe(grant -> {

if (grant) {

//获得权限

openAlbum();

} else {

showMsg(“未获取到权限”);

}

});

} else {

openAlbum();

}

}

面试复习路线,梳理知识,提升储备

自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。

关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

  • 架构师筑基必备技能
  • Android高级UI与FrameWork源码
  • 360°全方面性能调优
  • 解读开源框架设计思想
  • NDK模块开发
  • 微信小程序
  • Hybrid 开发与Flutter

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

Android开发七大模块核心知识笔记

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

rxPermissions = new RxPermissions(this);

下面修改布局,在之前的按钮后面再加一个按钮

<Button

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:onClick=“IdentifyAlbumPictures”

android:text=“识别相册图片” />

然后在MainActivity中增加IdentifyAlbumPictures方法,代码如下:

/**

  • 识别相册图片

  • @param view

*/

@SuppressLint(“CheckResult”)

public void IdentifyAlbumPictures(View view) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

rxPermissions.request(

Manifest.permission.READ_EXTERNAL_STORAGE,

Manifest.permission.WRITE_EXTERNAL_STORAGE)

.subscribe(grant -> {

if (grant) {

//获得权限

openAlbum();

} else {

showMsg(“未获取到权限”);

}

});

} else {

openAlbum();

}

}

面试复习路线,梳理知识,提升储备

自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。

关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

  • 架构师筑基必备技能
  • Android高级UI与FrameWork源码
  • 360°全方面性能调优
  • 解读开源框架设计思想
  • NDK模块开发
  • 微信小程序
  • Hybrid 开发与Flutter

[外链图片转存中…(img-senzpNZr-1714325743950)]

知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

[外链图片转存中…(img-x6WtmgZi-1714325743950)]

《960全网最全Android开发笔记》

[外链图片转存中…(img-2TFfAiJj-1714325743951)]

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

如何使用它?

1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数

[外链图片转存中…(img-zBcw8W0I-1714325743951)]

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值