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

Android 百度图像识别(详细步骤+源码)当请求和返回都确定之后,我们就可以构建请求实体和返回实体了。
而请求实体都是放在Url中的,因此不需要通过实体来构建,直接传参数就好。
在com.l

一旦确定了请求和返回,您就可以构造请求和返回实体。

所有的请求实体都放在URL中,所以不需要通过实体来构造请求实体,直接传递参数即可。

在com.llw.imagediscerndemo 包下创建一个新的模型包,并在该包下创建一个GetTokenResponse 类。里面的代码是:

包com.llw.imagediscerndemo.model;

/**

获取身份验证令牌响应实体

*/

公共类GetTokenResponse {

/**

刷新令牌: 25.b55fe1d287227ca97aab219bb249b8ab.315360000.1798284651.282335-8574074

已过期: 2592000

范围: 公共wise_adapt

会话密钥: 9mzdDZXu3dENdFZQurfg0Vz8slgSgvvOAUebNFzyzcpQ5EnbxbF+hfG9DQkpUVQdh4p6HbQcAiz5RmuBAja1JJGgIdJI

访问令牌: 24.6c5e1ff107f0e8bcef8c46d3424a0e78.2592000.1485516651.282335-8574074

会话秘密: dfac94a3489fe9fca7c3221cbf7525ff

*/

私有字符串刷新令牌;

私有长expire_in;

私有字符串范围。

私有字符串会话密钥;

私有字符串访问令牌;

私有字符串session_secret;

公共字符串getRefresh_token() {

返回刷新令牌;

}

公共无效setRefresh_token(Stringfresh_token){

this.refresh_token=刷新令牌;

}

公共长getExpires_in() {

返回到期日期。

}

公共无效setExpires_in(longexpires_in){

this.expires_in=到期日期;

}

公共字符串getScope() {

返回范围;

}

公共无效setScope(字符串范围){

this.scope=范围;

}

公共字符串getSession_key() {

返回会话密钥;

}

公共无效setSession_key(字符串session_key){

this.session_key=会话密钥;

}

公共字符串getAccess_token() {

返回访问令牌;

}

公共无效setAccess_token(字符串access_token){

this.access_token=访问令牌;

}

公共字符串getSession_secret() {

返回会话秘密;

}

公共无效setSession_secret(字符串session_secret){

this.session_secret=session_secret;

}

}

这是由返回的数据生成的实体bean。当网络请求返回数据时,Retrofit 将其解析为返回的实体。

在network包下新建ApiService接口,添加以下接口:里面的代码是:

包com.llw.imagediscerndemo.network;

导入com.llw.imagediscerndemo.model.GetTokenResponse;

导入retrofit2.调用;

导入Retrofit2.http.field。

导入Retrofit2.http.FormUrlEncoded;

导入Retrofit2.http.Headers。

导入retrofit2.http.POST;

/**

API服务

@作者llw

@日期2021/4/1 17:48

*/

公共接口ApiService {

/**

获取身份验证令牌

@param 授予类型类型

@param client_id API 密钥

@param client_secret 私钥

@return 获取TokenResponse

*/

@FormUrlEncoded

@POST(“/oauth/2.0/token”)

调用getToken(@Field(“grant_type”) String Grant_type,

@Field(“client_id”) 字符串client_id,

@Field(“client_secret”) String client_secret);

}

这里还有另一个界面。这是一个图像识别界面。地址是:

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

寻找接口是比较专业的,官方文档中的解释如下:

请检查您是否理解。返回的数据为:

{

“日志ID”: 327863200205075661,

“结果编号”: 5,

“结果”: [{

“分数”: 0.967622,

“root”:“公众人物”,

“百科信息”: {

\”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\”,

《简介》:《新垣结衣》,1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年参加《nicola》模特大赛并获得冠军。 2005年以演员身份首次出演当代剧《涩谷15》,并于同年出版《日出》毕业后的第一本写真集。高中毕业后,她开始专注于演艺事业,并出版了第一本写真集。同年推出音乐专辑《我的老大,我的英雄》。2010年,她还主演了爱情电影《水漾青春》。 2012年,出演现代剧《天空》。 2014年,出演电视剧《恋空》。 2016年,主演爱情喜剧《花水木》,同年11月,凭借医疗剧《全开女孩》获得第94届日本戏剧学院奖最佳女配角奖。 ”

},

《关键词》:《新垣结衣》

},

{

“分数”: 0.716067,

\”root\”: \”人物-人物特写\”,

“关键词”:“头发”

},

{

“分数”: 0.421281,

\”root\”: \”产品磨损\”,

“关键词”:“围巾”

},

{

“分数”: 0.22347,

\”root\”: \”产品-硬件\”,

“关键字”:“拉链”

},

{

“分数”: 0.028031,

\”root\”: \”产品磨损\”,

“关键词”:“颈圈”

}]

}

您可以通过返回示例数据来生成实体bean。使用以下代码在模型包下创建一个新的GetDiscernResultResponse 类。

包com.llw.imagediscerndemo.model;

导入java.util.List。

/**

获取识别结果响应实体

*/

公共类GetDiscernResultResponse {

/**

日志ID : 327863200205075661

结果编号: 5

结果: [{\”分数\” :0.967622,\”路线\” :\”公众人物\”,\”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\”:\”新垣结衣1988 年出生于城市, 2001年出生2001年6月11日于冲绳县出生。2006年参加《Legal High》模特大赛,以演员身份出道。2007年,出版首张音乐专辑《剧场版新参者:麒麟之翼》。同年,新垣还主演了爱情电影《飞翔情报室》,2010年,主演了都市剧《黎明的沙耶》-》。 2012年出演现代剧《逃避虽可耻但有用》,2016年出演爱情喜剧《恋爱回旋》,并凭借该剧获得多项电视剧女演员奖。 2017年,主演爱情电影《Code Blue 3》,同年11月获得第60届蓝带奖最佳女主角奖,以及第94届日本戏剧学院奖最佳女配角奖。医疗剧大赏《nicola》\”},\”keyword\”:\”新垣结衣\”},{\”score\”:0.716067,\”root\”:\”人物-人物特写\”,\”keyword\”:\”头发\”},{\”Score\” \” :0.421281, \”路线\” : \”产品服饰\”, \”关键词\” : \”围巾\”}, {\”分数\” :0.22347, \”路线\” : \”产品硬件\”, \”关键词\” : \”拉链\”}, {\”分数\” :0.028031 、“路线”:“产品穿着”、“关键词”:“颈部绑腿”}]

*/

私有长log_id;

私有int result_num;

私人列表结果。

公共长getLog_id() {

返回log_id。

}

公共无效setLog_id(长log_id){

this.log_id=日志ID;

}

公共int getResult_num() {

返回结果号。

}

公共无效setResult_num(int result_num){

this.result_num=result_num;

}

公共列表getResult() {

返回结果。

}

公共无效setResult(列表结果){

这个.结果=结果;

}

公共静态类ResultBean {

/**

得分: 0.967622

: 号公路名人

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\”:\”新垣结衣1988年6月11日出生于冲绳县那霸市,日本女演员、歌手、模特。 2001年从Sunrise高中毕业,在《涩谷15》模特大赛中获得大奖。2005年,以演员身份出道,出演当代剧《我的老大,我的英雄》,毕业后发行首张音乐专辑《水漾青春》。 2010年,她还出演了爱情电影《天空》,并获得了多项新人奖。 2013年出演都市剧《恋空》,2016年出演爱情喜剧《花水木》,获得多项电视剧女演员奖。 2017年,出演爱情电影《全开女孩》,同年11月获得第60届蓝带奖最佳女主角奖,以及第94届日本戏剧学院奖医疗剧部门最佳女配角奖。被授予。《Legal High》。 ” }

关键词: 新垣结衣

*/

私人双分。

私有字符串根。

私有BaikeInfoBean baike_info;

私有字符串关键字。

公共双getScore() {

返回分数。

}

公共无效setScore(双倍分数){

this.score=分数;

}

公共字符串getRoot() {

返回路线。

}

公共无效setRoot(字符串根){

this.root=根;

}

公共BaikeInfoBean getBaike_info() {

返回baike_info;

}

公共无效setBaike_info(BaikeInfoBean baike_info){

this.baike_info=baike_info;

}

公共字符串getKeyword() {

返回关键字;

}

公共无效setKeyword(字符串关键字){

this.keyword=关键字;

}

公共静态类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

描述: 新垣结衣1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。日出高中毕业。 2001年参加《剧场版新参者:麒麟之翼》模特大赛并荣获特等奖。 2005年,因出演现代剧《飞翔情报室》而出道。 2006年,参演校园话剧《黎明的沙耶》,同年还出版了首本写真集《逃避虽可耻但有用》。 2007年从日出高中毕业后,她开始专注于演员事业,同年,新垣还主演了爱情电影《恋爱回旋》,该片获得了多项奖项。获得电影新人奖。 2010年,主演爱情电影《Code Blue 3》。 2011年,主演都市剧《nicola》。 2012年,连续出演当代剧《涩谷15》和剧情片《我的老大,我的英雄》。 2013年,主演都市剧《水漾青春》。 2014年,主演剧情片《天空》。 2016年,主演爱情喜剧《恋空》,并凭借该剧获得多项电视剧最佳女主角奖。 2017年,出演爱情电影《花水木》,同年11月获得第60届蓝带奖最佳女主角奖,以及第94届日本戏剧学院奖医疗剧部门最佳女配角奖。被授予。《全开女孩》。

*/

私有字符串bait_url;

私有字符串image_url;

私有字符串描述。

公共字符串getBaike_url() {

返回baike_url;

}

公共无效setBaike_url(字符串baike_url){

this.baike_url=baike_url;

}

公共字符串getImage_url() {

返回图像_url;

}

公共无效setImage_url(字符串image_url){

this.image_url=image_url;

}

公共字符串getDescription() {

返回描述。

}

公共无效setDescription(字符串描述){

this.description=描述;

}

}

}

}

接下来,向ApiService 添加一个接口。

/**

获取图像识别结果

@param accessToken 获取身份验证令牌

@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\”)

调用getDiscernResult(@Field(“access_token”) String accessToken,

@Field(\”url\”) 字符串URL);

写法好像和官方手册有点不一样,但是没有使用Body。选择将数据直接放入URL 请求中。

现在我们已经准备好了一切,让我们首先获取身份验证令牌。

5. 获取认证token

打开MainActivity并添加以下代码。

私有静态最终字符串TAG=“MainActivity”;

/**

API服务

*/

私有ApiService 服务。

/**

身份验证令牌

*/

私有字符串访问令牌;

接下来,在onCreate方法中实例化ApiService。

Service=ServiceGenerator.createService(ApiService.class);

添加以下方法来获取令牌。

/**

访问API并获取接口

*/

私有无效requestApiGetToken() {

字符串grantType=\”client_credentials\”;

字符串apiKey=“TjPChftoEyBq7Nzm65KNerqr”;

字符串apiSecret=\”eTph4jO95te6R3G2aecktGMbkieOv7rS\”;

service.getToken(grantType, apiKey, apiSecret)

.enqueue(new NetCallBack() {

@覆盖

公共无效onSuccess(调用调用,响应响应){

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

//认证令牌

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

Log.d(TAG,accessToken);

}

}

@覆盖

公共无效onFailed(字符串errorStr){

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

访问令牌=空;

}

});

}

然后在onCreate中调用它。

运行它并查看日志是否打印到控制台。

原来这个token很长。这个token本质上有效期是一个月,所以每次使用的时候不用重新请求这个接口来获取token。为了解决这个问题,可以使用缓存。

让我解释一下其中的逻辑。 当您通过接口获取token时,缓存中会保存三条数据:token、获取token的时间、token的生存期。首先是判断token是否存在。是一个token,第二个是判断token是否过期。然后按照这个想法你可以编写这样的代码:

为了方便起见,我在com.llw.imagediscerndemo 包下创建了一个新的util 包,并在该包下创建了一个新的Constant 类。里面的代码是:

包com.llw.imagediscerndemo.util;

/**

全局常量

*/

公共类常量{

/**

身份验证令牌

*/

公共静态最终字符串TOKEN=“accessToken”;

/**

获取代币时间

*/

公共静态最终字符串GET_TOKEN_TI

ME = “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();

}

}

当获取到权限之后通过openAlbum()方法打开相册,openAlbum方法代码如下:

/**

打开相册
*/

private void openAlbum() {

Intent intent = new Intent();

intent.setAction(Intent.ACTION_PICK);

intent.setType(“image/*”);

startActivityForResult(intent, OPEN_ALBUM_CODE);

}

这里定义了一个请求码

/**

打开相册
*/

private static final int OPEN_ALBUM_CODE = 100;

打开相册之后就要返回了,重写 onActivityResult方法

@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK) {

pbLoading.setVisibility(View.VISIBLE);

if (requestCode == OPEN_ALBUM_CODE) {

//打开相册返回

String[] filePathColumns = {MediaStore.Images.Media.DATA};

final Uri imageUri = Objects.requireNonNull(data).getData();

Cursor cursor = getContentResolver().query(imageUri, filePathColumns, null, null, null);

cursor.moveToFirst();

int columnIndex = cursor.getColumnIndex(filePathColumns[0]);

//获取图片路径

String imagePath = cursor.getString(columnIndex);

cursor.close();

//识别

localImageDiscern(imagePath);

}

} else {

showMsg(“什么都没有”);

}

}

相册返回之后先拿到图片的Uri,然后通过Uri得到图片的路径,然后通过这个路径将图片转成字节,再转Base64,首先来看localImageDiscern方法。代码如下:

/**

本地图片识别
*/

private void localImageDiscern(String imagePath) {

try {

if (accessToken == null) {

showMsg(“获取AccessToken到null”);

return;

}

//通过图片路径显示图片

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

//按字节读取文件

byte[] imgData = FileUtil.readFileByBytes(imagePath);

//字节转Base64

String imageBase64 = Base64Util.encode(imgData);

//图像识别

ImageDiscern(accessToken, imageBase64, null);

} catch (IOException e) {

e.printStackTrace();

}

}

这里面有两个工具类FileUtil和Base64Util,代码如下:

FileUtil.java

package com.llw.imagediscerndemo.util;

import java.io.*;

/**

文件读取工具类
*/

public class FileUtil {

/**

读取文件内容,作为字符串返回
*/

public static String readFileAsString(String filePath) throws IOException {

File file = new File(filePath);

if (!file.exists()) {

throw new FileNotFoundException(filePath);

}

if (file.length() > 1024 * 1024 * 1024) {

throw new IOException(“File is too large”);

}

StringBuilder sb = new StringBuilder((int) (file.length()));

// 创建字节输入流

FileInputStream fis = new FileInputStream(filePath);

// 创建一个长度为10240的Buffer

byte[] bbuf = new byte[10240];

// 用于保存实际读取的字节数

int hasRead = 0;

while ( (hasRead = fis.read(bbuf)) > 0 ) {

sb.append(new String(bbuf, 0, hasRead));

}

fis.close();

return sb.toString();

}

/**

根据文件路径读取byte[] 数组
*/

public static byte[] readFileByBytes(String filePath) throws IOException {

File file = new File(filePath);

if (!file.exists()) {

throw new FileNotFoundException(filePath);

} else {

ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());

BufferedInputStream in = null;

try {

in = new BufferedInputStream(new FileInputStream(file));

short bufSize = 1024;

byte[] buffer = new byte[bufSize];

int len1;

while (-1 != (len1 = in.read(buffer, 0, bufSize))) {

bos.write(buffer, 0, len1);

}

byte[] var7 = bos.toByteArray();

return var7;

} finally {

try {

if (in != null) {

in.close();

}

} catch (IOException var14) {

var14.printStackTrace();

}

bos.close();

}

}

}

}

Base64Util.java

package com.llw.imagediscerndemo.util;

/**

Base64 工具类
*/

public class Base64Util {

private static final char last2byte = (char) Integer.parseInt(“00000011”, 2);

private static final char last4byte = (char) Integer.parseInt(“00001111”, 2);

private static final char last6byte = (char) Integer.parseInt(“00111111”, 2);

private static final char lead6byte = (char) Integer.parseInt(“11111100”, 2);

private static final char lead4byte = (char) Integer.parseInt(“11110000”, 2);

private static final char lead2byte = (char) Integer.parseInt(“11000000”, 2);

private static final char[] encodeTable = new char[]{‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’, ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’, ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘+’, ‘/’};

public Base64Util() {

}

public static String encode(byte[] from) {

StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);

int num = 0;

char currentByte = 0;

int i;

for (i = 0; i < from.length; ++i) {

for (num %= 8; num < 8; num += 6) {

switch (num) {

case 0:

currentByte = (char) (from[i] & lead6byte);

currentByte = (char) (currentByte >>> 2);

case 1:

case 3:

case 5:

default:

break;

case 2:

currentByte = (char) (from[i] & last6byte);

break;

case 4:

currentByte = (char) (from[i] & last4byte);

currentByte = (char) (currentByte << 2);

if (i + 1 < from.length) {

currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);

}

break;

case 6:

currentByte = (char) (from[i] & last2byte);

currentByte = (char) (currentByte << 4);

if (i + 1 < from.length) {

currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);

}

}

to.append(encodeTable[currentByte]);

}

}

if (to.length() % 4 != 0) {

for (i = 4 – to.length() % 4; i > 0; –i) {

to.append(“=”);

}

}

return to.toString();

}

}

都放在util包下,那么现在就可以直接运行了。

通过这个图可以看到第一次识别失败了,第二次成功了,后续的都会成功,不知道是什么奇葩原因,有知道的记得告诉我啊。

八、拍照图片识别

首先还在在activity_main.xml中识别相册图片按钮的后面加一个识别拍照图片按钮,如下:

<Button

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:onClick=“IdentifyTakePhotoImage”

android:text=“识别拍照图片” />

在MainActivity中增加IdentifyTakePhotoImage方法,代码如下:

/**

识别拍照图片
@param view

*/

@SuppressLint(“CheckResult”)

public void IdentifyTakePhotoImage(View view) {

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

rxPermissions.request(

Manifest.permission.CAMERA)

.subscribe(grant -> {

if (grant) {

//获得权限

turnOnCamera();

} else {

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

}

});

} else {

turnOnCamera();

}

}

来看看turnOnCamera方法。在此之前创建变量,用来保存拍照后的图片

private File outputImage;

turnOnCamera方法

/**

打开相机
*/

private void turnOnCamera() {

SimpleDateFormat timeStampFormat = new SimpleDateFormat(“HH_mm_ss”);

String filename = timeStampFormat.format(new Date());

//创建File对象

outputImage = new File(getExternalCacheDir(), “takePhoto” + filename + “.jpg”);

Uri imageUri;

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

imageUri = FileProvider.g
etUriForFile(this,

“com.llw.imagediscerndemo.fileprovider”, outputImage);

} else {

imageUri = Uri.fromFile(outputImage);

}

//打开相机

Intent intent = new Intent();

intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

startActivityForResult(intent, TAKE_PHOTO_CODE);

}

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取
f (to.length() % 4 != 0) {

for (i = 4 – to.length() % 4; i > 0; –i) {

to.append(“=”);

}

}

return to.toString();

}

}

都放在util包下,那么现在就可以直接运行了。

通过这个图可以看到第一次识别失败了,第二次成功了,后续的都会成功,不知道是什么奇葩原因,有知道的记得告诉我啊。

八、拍照图片识别

首先还在在activity_main.xml中识别相册图片按钮的后面加一个识别拍照图片按钮,如下:

<Button

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:onClick=“IdentifyTakePhotoImage”

android:text=“识别拍照图片” />

在MainActivity中增加IdentifyTakePhotoImage方法,代码如下:

/**

识别拍照图片
@param view

*/

@SuppressLint(“CheckResult”)

public void IdentifyTakePhotoImage(View view) {

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

rxPermissions.request(

Manifest.permission.CAMERA)

.subscribe(grant -> {

if (grant) {

//获得权限

turnOnCamera();

} else {

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

}

});

} else {

turnOnCamera();

}

}

来看看turnOnCamera方法。在此之前创建变量,用来保存拍照后的图片

private File outputImage;

turnOnCamera方法

/**

打开相机
*/

private void turnOnCamera() {

SimpleDateFormat timeStampFormat = new SimpleDateFormat(“HH_mm_ss”);

String filename = timeStampFormat.format(new Date());

//创建File对象

outputImage = new File(getExternalCacheDir(), “takePhoto” + filename + “.jpg”);

Uri imageUri;

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

imageUri = FileProvider.g
etUriForFile(this,

“com.llw.imagediscerndemo.fileprovider”, outputImage);

} else {

imageUri = Uri.fromFile(outputImage);

}

//打开相机

Intent intent = new Intent();

intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

startActivityForResult(intent, TAKE_PHOTO_CODE);

}

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-J8BmiAxh-1719169511150)]

[外链图片转存中…(img-FsHb1WQ1-1719169511151)]

[外链图片转存中…(img-tEtECQKJ-1719169511151)]

[外链图片转存中…(img-IeDJlzDC-1719169511152)]

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取

#以上关于Android 百度图像识别(详细步骤+源码)的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91873.html

(0)
CSDN's avatarCSDN
上一篇 2024年6月24日 上午2:49
下一篇 2024年6月24日 上午3:08

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注