Android 垃圾分类APP垃圾分类之图像输入

Posted 初学者_Study

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 垃圾分类APP垃圾分类之图像输入相关的知识,希望对你有一定的参考价值。


图像输入

  • ​​前言​​
  • ​​正文​​
  • ​​一、创建平台应用​​
  • ​​二、新建图像识别页面​​
  • ​​三、网络订阅​​
  • ​​四、编写页面代码​​
  • ​​五、识别网络图片​​
  • ​​六、识别相册图片​​
  • ​​七、识别拍照图片​​
  • ​​八、垃圾分类​​
  • ​​九、源码​​

前言

  在上一篇文章中完成了语音输入,这一篇来写图像输入

正文

  图像输入无非就是图片识别嘛,再通俗一点就是识别手机中的照片,分析里面的物品,然后进行垃圾分类。图像识别还是有很多的SDK可以使用的,这里面我目前用过的就是百度的图像识别,感觉还是蛮好的,而且有我之前的文章做普遍,那么本文是属于APP功能编写,这与单独写介绍SDK使用的文章完全是两回事。那么就来看看实践中怎么插入这个图像识别了。

如果你还有时间的话,不妨先去看看​​android 百度图像识别(详细步骤+源码)​​

因为毕竟是写过一次的东西了,只是应用环境不同,所以下面就只是介绍业务逻辑和贴代码,不再去详细讲解。

一、创建平台应用

既然要用百度的SDK,自然要先去​​百度智能云​​注册登录,登录之后呢。点击管理控制台,然后点击左侧产品服务箭头左侧展开,找到图像识别点进去。

Android


点击创建应用

Android


输入相关的信息就可以了。

Android


填写好资料后点击立即创建。

Android


查看应用详情。

Android


这里有三个关键的信息:AppID、API Key、Secret Key,这三个值在后面会用到,请使用自己创建应用时生成的值。现在先把它们放到常量里面,打开Constant,这里的四个常量,对应的值就是你在平台上申请应用产生的,记得使用自己的。

Android

二、新建图像识别页面

在ui包下新建一个ImageInputActivity,对应的xml为activity_image_input.xml,创建好之后,再MainActivity页面中写一个按钮,点击之后进入刚才创建的这个图像识别页面。
修改activity_main.xml,在语音输入的下面加一个图像输入的按钮,代码如下:

"@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_60"
android:layout_margin="@dimen/dp_16"
android:gravity="center"
android:insetTop="@dimen/dp_0"
android:insetBottom="@dimen/dp_0"
android:onClick="jumpImageInput"
android:text="图像输入"
android:textSize="@dimen/sp_16"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar"
app:backgroundTint="@color/colorPrimaryDark"
app:cornerRadius="@dimen/dp_12"
app:icon="@mipmap/icon_image_input"
app:iconGravity="textStart"
app:iconSize="@dimen/dp_24"

进入到MainActivity中,新增一个方法jumpImageInput。

/**
* 进入图像输入页面
*/
public void jumpImageInput(View view)
gotoActivity(ImageInputActivity.class);

下面来写ImageInputActivity页面的代码,写代码之前,先完成布局编写,修改activity_image_input,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<!--标题-->
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/white"
android:elevation="@dimen/dp_2"
app:navigationIcon="@mipmap/icon_back">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="图像输入"
android:textColor="@color/black"
android:textSize="18sp" />
</com.google.android.material.appbar.MaterialToolbar>
<!--滑动控件-->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">

<ImageView
android:id="@+id/iv_picture"
android:layout_width="@dimen/dp_200"
android:layout_height="@dimen/dp_300"
android:layout_marginTop="@dimen/dp_12"
android:visibility="gone" />

<EditText
android:id="@+id/et_image_url"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_50"
android:background="@drawable/shape_et_bg"
android:hint="网络图片Url"
android:layout_margin="@dimen/dp_1"
android:textCursorDrawable="@drawable/cursor_style"
android:paddingStart="@dimen/dp_12"
android:paddingEnd="@dimen/dp_12"
android:singleLine="true"
android:imeOptions="actionGo"
android:textSize="@dimen/sp_14"
android:visibility="gone" />


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/dp_12">

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_web_picture"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="0dp"
android:layout_height="@dimen/dp_50"
android:layout_marginEnd="@dimen/dp_6"
android:layout_weight="1"
android:gravity="center"
android:insetTop="@dimen/dp_0"
android:insetBottom="@dimen/dp_0"
android:text="网络图片"
android:textSize="@dimen/sp_16"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar"
app:backgroundTint="@color/colorPrimaryDark"
app:cornerRadius="@dimen/dp_12"
app:iconGravity="textStart"
app:iconSize="@dimen/dp_24" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_open_album"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="0dp"
android:layout_height="@dimen/dp_50"
android:layout_marginStart="@dimen/dp_6"
android:layout_marginEnd="@dimen/dp_6"
android:layout_weight="1"
android:gravity="center"
android:insetTop="@dimen/dp_0"
android:insetBottom="@dimen/dp_0"
android:text="相册图片"
android:textSize="@dimen/sp_16"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar"
app:backgroundTint="@color/colorPrimaryDark"
app:cornerRadius="@dimen/dp_12"
app:iconGravity="textStart"
app:iconSize="@dimen/dp_24" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_take_photo"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="0dp"
android:layout_height="@dimen/dp_50"
android:layout_marginStart="@dimen/dp_6"
android:layout_weight="1"
android:gravity="center"
android:insetTop="@dimen/dp_0"
android:insetBottom="@dimen/dp_0"
android:text="拍照图片"
android:textSize="@dimen/sp_16"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar"
app:backgroundTint="@color/colorPrimaryDark"
app:cornerRadius="@dimen/dp_12"
app:iconGravity="textStart"
app:iconSize="@dimen/dp_24" />
</LinearLayout>
<!--图像识别结果-->
<LinearLayout
android:id="@+id/lay_recognition_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="gone">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">

<View
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_1"
android:background="@color/line_color" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/dp_12"
android:text="识别结果" />

<View
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_1"
android:background="@color/line_color" />
</LinearLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_recognition_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never" />
</LinearLayout>
<!--垃圾分类结果-->
<LinearLayout
android:id="@+id/lay_classification_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="gone">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">

<View
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_1"
android:background="@color/line_color" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/dp_12"
android:text="分类结果" />

<View
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_1"
android:background="@color/line_color" />
</LinearLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_classification_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"

然后回到ImageInputActivity页面,再写代码之前,先想一下这个页面要做什么?首先是获取百度的鉴权Token,然后进行图片识别,最后进行物品的垃圾分类。那么就需要三次网络请求,这里需要重写一个订阅。

三、网络订阅

这里需要增加一个网络访问地址,因为使用的是百度的API,而本身有一个天行的API地址,这里需要对两个地址进行一个控制。打开NetworkApi,在里面增加如下方法。

/**
* 修改访问地址
* @param type
*/
private static void getBaseUrl(int type)
switch (type)
case 0://天行API地址
mBaseUrl = "http://api.tianapi.com";
break;
case 1://百度SDK地址
mBaseUrl = "https://aip.baidubce.com";
break;
default:
break;

这个方法根据传入类型的不同,使用不同的网络地址,之前写博客时疏忽了,写的嗨了,漏掉了这一部分,现在补上。然后在createService方法中增加一个type参数,之后调用getBaseUrl方法获取访问地址。

Android


现在你的这个createService方法改动了,那么其他调用了这个方法的地方也要做相应的改动,比如之前在做文字输入进行垃圾分类识别时,TextContract中的调用,之前是没有type的,现在你加一个0就可以了,0表示就是访问天行API。

Android


其他的地方记也要修改,否则会报错的。改好之后,就可以来写这个图像识别到的订阅器了,如下:

在contract包下新建一个ImageContract类,里面的代码如下:

package com.llw.goodtrash.contract;

import android.annotation.SuppressLint;

import com.llw.goodtrash.api.ApiService;
import com.llw.goodtrash.model.GetDiscernResultResponse;
import com.llw.goodtrash.model.GetTokenResponse;
import com.llw.goodtrash.model.TrashResponse;
import com.llw.mvplibrary.base.BasePresenter;
import com.llw.mvplibrary.base.BaseView;
import com.llw.mvplibrary.network.NetworkApi;
import com.llw.mvplibrary.network.observer.BaseObserver;

import static com.llw.goodtrash.utils.Constant.*;

/**
* 图像输入页面访问网络
*
* @author llw
* @date 2021/3/30 15:28
*/
public class ImageContract

public static class ImagePresenter extends BasePresenter<ImageView>

/**
* 获取鉴权Token
*/
@SuppressLint("CheckResult")
public void getToken()
ApiService service = NetworkApi.createService(ApiService.class, 1);
service.getToken(GRANT_TYPE, API_KEY, API_SECRET)
.compose(NetworkApi.applySchedulers(new BaseObserver<GetTokenResponse>()
@Override
public void onSuccess(GetTokenResponse getTokenResponse)
if (getView() != null)
getView().getTokenResponse(getTokenResponse);



@Override
public void onFailure(Throwable e)
if (getView() != null)
getView().getTokenFailed(e);


));



/**
* 获取图像识别结果
*
* @param token 鉴权Token
* @param image 图片base64
* @param url 网络图片url
*/
@SuppressLint("CheckResult")
public void getDiscernResult(String token, String image, String url)
ApiService service = NetworkApi.createService(ApiService.class, 1);
service.getDiscernResult(token, image, url)
.compose(NetworkApi.applySchedulers(new BaseObserver<GetDiscernResultResponse>()
@Override
public void onSuccess(GetDiscernResultResponse getTokenResponse)
if (getView() != null)
getView().getDiscernResultResponse(getTokenResponse);



@Override
public void onFailure(Throwable e)
if (getView() != null)
getView().getDiscernResultFailed(e);


));



/**
* 搜索物品
*
* @param word 物品名
*/
@SuppressLint("CheckResult")
public void searchGoods(String word)
ApiService service = NetworkApi.createService(ApiService.class, 0);
service.searchGoods(word).compose(NetworkApi.applySchedulers(new BaseObserver<TrashResponse>()
@Override
public void onSuccess(TrashResponse groupResponse)
if (getView() != null)
getView().getSearchResponse(groupResponse);



@Override
public void onFailure(Throwable e)
if (getView() != null)
getView().getSearchResponseFailed(e);


));



public interface ImageView extends BaseView
/**
* 获取鉴权Token
*
* @param response GetTokenResponse
*/
void getTokenResponse(GetTokenResponse response);

/**
* 获取鉴权Token异常返回
*
* @param throwable 异常
*/
void getTokenFailed(Throwable throwable);

/**
* 获取图像识别结果
*
* @param response GetDiscernResultResponse
*/
void getDiscernResultResponse(GetDiscernResultResponse response);

/**
* 获取图像识别结果失败
*
* @param throwable 异常
*/
void getDiscernResultFailed(Throwable throwable);

/**
* 搜索物品返回
*
* @param response TrashResponse
*/
void getSearchResponse(TrashResponse response);

/**
* 搜索物品异常返回
*
* @param throwable 异常
*/
void getSearchResponseFailed(Throwable throwable);

鉴权方法中的几个全局变量在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";

/**
* 百度鉴权认证参数值
*/
public static final String GRANT_TYPE = "client_credentials";

/**
* 百度图像识别 APP ID GoodTrash
*/
public static final String APP_ID = "23943795";

/**
* 百度图像识别 APP Key GoodTrash
*/
public static final String API_KEY = "PAUCX7vSAd4ZBwv897GAfhEQ";

请注意,这里的值是我在百度开放平台上注册应用时生成的,请替换为自己的。

下面回到ImageInputActivity,修改代码后如下:

package com.llw.goodtrash.ui;

import android.os.Bundle;
import com.llw.goodtrash.R;
import com.llw.goodtrash.contract.ImageContract;
import com.llw.goodtrash.model.GetDiscernResultResponse;
import com.llw.goodtrash.model.GetTokenResponse;
import com.llw.goodtrash.model.TrashResponse;
import com.llw.mvplibrary.mvp.MvpActivity;

/**
* 图像输入物品进行垃圾分类
*
* @author llw
* @date 2021/4/7 11:04
*/
public class ImageInputActivity extends MvpActivity<ImageContract.ImagePresenter> implements ImageContract.ImageView

@Override
public void initData(Bundle savedInstanceState)



@Override
public int getLayoutId()
return R.layout.activity_image_input;


@Override
protected ImageContract.ImagePresenter createPresenter()
return new ImageContract.ImagePresenter();



@Override
public void getTokenResponse(GetTokenResponse response)



@Override
public void getTokenFailed(Throwable throwable)



@Override
public void getDiscernResultResponse(GetDiscernResultResponse response)



@Override
public void getDiscernResultFailed(Throwable throwable)



@Override
public void getSearchResponse(TrashResponse response)



@Override
public void getSearchResponseFailed(Throwable throwable)




这里使用了MVP,通过P来处理M和V,三个网络请求对应六个返回,下面进行页面的初始化

四、编写页面代码

先声明一些变量

private static final String TAG = "ImageInputActivity";
/**
* 打开相册
*/
private static final int OPEN_ALBUM_CODE = 100;
/**
* 打开相机
*/
private static final int TAKE_PHOTO_CODE = 101;
/**
* 鉴权Toeken
*/
private String accessToken;

private Toolbar toolbar;
private ImageView ivPicture;
private EditText etImageUrl;
private LinearLayout layRecognitionResult,layClassificationResult;
private RecyclerView rvRecognitionResult,rvClassificationResult;
private RxPermissions rxPermissions;

private File outputImage;

然后新增一个initView的方法。

/**
* 初始化
*/
private void以上是关于Android 垃圾分类APP垃圾分类之图像输入的主要内容,如果未能解决你的问题,请参考以下文章

图像识别基于卷积神经网络(CNN)实现垃圾分类Matlab源码

图像识别基于卷积神经网络(CNN)实现垃圾分类Matlab源码

图像识别基于卷积神经网络(CNN)实现垃圾分类Matlab源码

环保垃圾分类监测终端

Android Studio实现一个垃圾分类系统(Kotlin版本)

Android Studio实现一个垃圾分类系统(Kotlin版本)