前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 百度图像识别(详细步骤+源码)(下)

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

原创
作者头像
晨曦_LLW
修改2021-04-22 10:08:42
1.4K3
修改2021-04-22 10:08:42
举报

五、获取鉴权认证Token

打开MainActivity,添加如下代码:

代码语言:txt
复制
	private static final String TAG = "MainActivity";
    /**
     * Api服务
     */
    private ApiService service;
    /**
     * 鉴权Toeken
     */
    private String accessToken;

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

代码语言:txt
复制
	service = ServiceGenerator.createService(ApiService.class);

新增如下方法获取Token,

代码语言:txt
复制
	/**
     * 访问API获取接口
     */
    private void requestApiGetToken() {
        String grantType = "client_credentials";
        String apiKey = "TjPChftoEyBq7Nzm65KNerqr";
        String apiSecret = "eTph4jO95te6R3G2aecktGMbkieOv7rS";
        service.getToken(grantType, apiKey, apiSecret)
                .enqueue(new NetCallBack<GetTokenResponse>() {
                    @Override
                    public void onSuccess(Call<GetTokenResponse> call, Response<GetTokenResponse> 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类,里面的代码如下:

代码语言:txt
复制
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工具类,里面的代码如下:

代码语言:txt
复制
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中的代码,如下。

代码语言:txt
复制
					@Override
                    public void onSuccess(Call<GetTokenResponse> call, Response<GetTokenResponse> 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是否过期的方法,方法代码如下:

代码语言:txt
复制
	/**
     * 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,同时将我们之前写的代码给串起来。

代码语言:txt
复制
	/**
     * 获取鉴权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。

六、网络图片Url识别

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

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

代码语言:txt
复制
https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg

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

在这里插入图片描述
在这里插入图片描述

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

代码语言:txt
复制
<?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="识别网络图片" />

    </LinearLayout>

    <ProgressBar
        android:visibility="gone"
        android:id="@+id/pb_loading"
        android:layout_centerInParent="true"
        android:layout_width="60dp"
        android:layout_height="60dp"/>


</RelativeLayout>

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

代码语言:txt
复制
	/**
     * 识别网络图片
     *
     * @param view
     */
    public void IdentifyWebPictures(View view) {
    	
    }

首先创建对象

代码语言:txt
复制
	/**
     * 显示图片
     */
    private ImageView ivPicture;
    /**
     * 进度条
     */
    private ProgressBar pbLoading;

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

代码语言:txt
复制
		ivPicture = findViewById(R.id.iv_picture);
        pbLoading = findViewById(R.id.pb_loading);

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

代码语言:txt
复制
	public void IdentifyWebPictures(View view) {
        pbLoading.setVisibility(View.VISIBLE);
        String token = getAccessToken();
        String imgUrl = "https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg";
        //显示图片
        Glide.with(this).load(imgUrl).into(ivPicture);
        showMsg("图像识别中");
        service.getDiscernResult(token, imgUrl).enqueue(new NetCallBack<GetDiscernResultResponse>() {
            @Override
            public void onSuccess(Call<GetDiscernResultResponse> call, Response<GetDiscernResultResponse> 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,之后显示网络图片在ImageView控件中,Toast提示一下,之后请求的成功和失败的回调了,在成功的回调中先判断数据是否为空,不为空再通过showDiscernResult()方法去显示数据,下面写这个方法。

代码语言:txt
复制
	/**
     * 显示识别的结果列表
     *
     * @param result
     */
    private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {
        
    }

showMsg方法:

代码语言:txt
复制
	/**
     * Toast提示
     * @param msg 内容
     */
    private void showMsg(String msg){
        Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
    }

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

代码语言:txt
复制
<?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"/>
</RelativeLayout>

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

代码语言:txt
复制
<?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" />

</LinearLayout>

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

代码语言:txt
复制
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中,首先创建对象

代码语言:txt
复制
	/**
     * 底部弹窗
     */
    private BottomSheetDialog bottomSheetDialog;
    /**
     * 弹窗视图
     */
    private View bottomView;

然后在onCreate中实例化,

代码语言:txt
复制
	bottomSheetDialog = new BottomSheetDialog(this);
    bottomView = getLayoutInflater().inflate(R.layout.dialog_bottom, null);

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

代码语言:txt
复制
	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方法。

代码语言:txt
复制
	/**
     * 图像识别请求
     *
     * @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<GetDiscernResultResponse>() {
            @Override
            public void onSuccess(Call<GetDiscernResultResponse> call, Response<GetDiscernResultResponse> 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中声明:

代码语言:txt
复制
	private RxPermissions rxPermissions;

然后在onCreate中实例化

代码语言:txt
复制
	rxPermissions = new RxPermissions(this);

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

代码语言:txt
复制
	<Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="IdentifyAlbumPictures"
            android:text="识别相册图片" />

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

代码语言:txt
复制
	/**
     * 识别相册图片
     *
     * @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方法代码如下:

代码语言:txt
复制
	/**
     * 打开相册
     */
    private void openAlbum() {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_PICK);
        intent.setType("image/*");
        startActivityForResult(intent, OPEN_ALBUM_CODE);
    }

这里定义了一个请求码

代码语言:txt
复制
	/**
     * 打开相册
     */
    private static final int OPEN_ALBUM_CODE = 100;

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

代码语言:txt
复制
	@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方法。代码如下:

代码语言:txt
复制
	/**
     * 本地图片识别
     */
    private void localImageDiscern(String imagePath) {
        try {
            String token = getAccessToken();
            //通过图片路径显示图片
            Glide.with(this).load(imagePath).into(ivPicture);
            //按字节读取文件
            byte[] imgData = FileUtil.readFileByBytes(imagePath);
            //字节转Base64
            String imageBase64 = Base64Util.encode(imgData);
            //图像识别
            ImageDiscern(token, imageBase64, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

FileUtil.java

代码语言:txt
复制
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

代码语言:txt
复制
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中识别相册图片按钮的后面加一个识别拍照图片按钮,如下:

代码语言:txt
复制
		<Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="IdentifyTakePhotoImage"
            android:text="识别拍照图片" />

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

代码语言:txt
复制
	/**
     * 识别拍照图片
     *
     * @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方法。在此之前创建变量,用来保存拍照后的图片

代码语言:txt
复制
	private File outputImage;

turnOnCamera方法

代码语言:txt
复制
	/**
     * 打开相机
     */
    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.getUriForFile(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);
    }

这里同样配置了一个打开相机的请求码

代码语言:txt
复制
	/**
     * 打开相机
     */
    private static final int TAKE_PHOTO_CODE = 101;

下面进入到onActivityResult方法,加一个条件分支。

在这里插入图片描述
在这里插入图片描述

通过这个图片保存文件得到图片的路径,然后通过localImageDiscern()方法对这个路径下的文件进行处理,和打开相册之后拿到路径之后调用的是同一个方法。下面来运行一下:

在这里插入图片描述
在这里插入图片描述

嗯,那么到这里整个Demo就写完了,是不是还挺简单的,只要思路明确再加上细节处理的到位,任何的功能都不在话下,对吧。

九、源码

考虑到有时候GitHub会抽风,因此加上了CSDN的资源下载地址,我设置的0积分,随便下载。

如果你用的好,不妨给我的源码或者资源点个赞给个好评啥的。

GitHub源码地址:ImageDiscernDemo

CSDN资源地址:ImageDiscernDemo.rar

我是晨曦_LLW,山高水长,后会有期~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 五、获取鉴权认证Token
  • 六、网络图片Url识别
  • 七、相册图片识别
  • 八、拍照图片识别
  • 九、源码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档