Android对接EasyDl零门槛AI平台

478 阅读4分钟

AI平台对接

EasyDl零门槛AI平台

EasyDL从2017年11月中旬起,在国内率先推出针对AI零算法基础或者追求高效率开发的企业用户的零门槛AI开发平台,提供从数据采集、标注、清洗到模型训练、部署的一站式AI开发能力。对于各行各业有定制AI需求的企业用户来说,无论您是否具备AI基础,EasyDL设计简约,极易理解,最快5分钟即可上手学会,15分钟完成模型训练。

该平台还提供大量的AI应用场景

image.png

今天对接的功能是EasyDL图像中的物品检测

数据创建标注

进入物品检测平台后建造数据模型

image.png

创建完成之后进行图片导入,我们要检测的是猫狗杯子,所以将图片选择合适的方式进行导入

image.png

对图片进行标注每个种类标注几十张图片,然后交给平台智能标注即可

image.png

将数据标注完成之后,去我的模型中创建模型,选择数据集进行训练

image.png

训练完成就去发布模型,就可以将模型部署出来啦

image.png 经过一系列的操作之后发布出来的API就可以供我们使用了

使用EasySDL平台的API

EasyDL零门槛AI开发平台 - 图像分类API调用文档

根据平台指引拿到token

image.png

image.png

服务器返回的JSON文本参数如下:

  • access_token:  要获取的Access Token;
  • expires_in:  Access Token的有效期(秒为单位,有效期30天);
  • 其他参数忽略,暂时不用;

对接Android

创建项目,添加相关权限

<!--    文件读写权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--    相机调用-->
<uses-permission android:name="android.permission.CAMERA" />
<!--    联网-->
<uses-permission android:name="android.permission.INTERNET"/>

导入第三方依赖

// PictureSelector 基础 (必须)
implementation 'io.github.lucksiege:pictureselector:v3.10.5'
// 图片压缩 (按需引入)
implementation 'io.github.lucksiege:compress:v3.10.4'
// 图片裁剪 (按需引入)
implementation 'io.github.lucksiege:ucrop:v3.10.4'
// 自定义相机 (按需引入)
implementation 'io.github.lucksiege:camerax:v3.10.4'
//coil
implementation("io.coil-kt:coil:2.1.0")
//okhttp
implementation("com.squareup.okhttp3:okhttp:4.10.0")

使用PictureSelector图片选择库进行拍照、相册选图

PictureSelector/README_CN.md at version_component · LuckSiege/PictureSelector · GitHub

布局

image.png

拍照与相册代码

camerabtn.setOnClickListener {
    //拍照选择图片
    PictureSelector.create(this)
        .openCamera(SelectMimeType.ofImage())
        .forResult(object : OnResultCallbackListener<LocalMedia?> {
            override fun onResult(result: ArrayList<LocalMedia?>?) {
                image1.load(result?.get(0)?.path)
            }
            override fun onCancel() {

            }
        })
}
photobtn.setOnClickListener {
    //相册选择图片
    PictureSelector.create(this)
        .openSystemGallery(SelectMimeType.ofImage())
        .forSystemResult(object : OnResultCallbackListener<LocalMedia?> {
            override fun onResult(result: ArrayList<LocalMedia?>?) {

            }
            override fun onCancel() {

            }
        })
}

编写String Path 转 Bitmap 转 Base64 工具类

//path转Base64
fun String.pathToBitmap(): Bitmap? {
    val file = File(this)
    var bitmap: Bitmap? = null
    if(file.exists()){
        bitmap = BitmapFactory.decodeFile(this)
    }
    return bitmap
}
//bitmap转Base64
fun Bitmap.bitToBase():String?{
    var string:String?
    val bStream = ByteArrayOutputStream()
    this.compress(Bitmap.CompressFormat.PNG,100,bStream)
    var byte = bStream.toByteArray()
    string = Base64.encodeToString(byte,Base64.DEFAULT)
    return string
}
//Base64转Bitmap
fun String.baseToBit():Bitmap?{
    var decode = Base64.decode(this, Base64.DEFAULT)
    var bitmap = BitmapFactory.decodeByteArray(decode,0,decode.size)
    return bitmap
}

fun String.pathToBase():String?{
    var pathToBitmap = this.pathToBitmap()
    var bitToBase = pathToBitmap?.bitToBase()
    return bitToBase
}

成功拿到Base64编码之后就可以开始请求EasyDL平台接入

因为个人原因,可能是https的缘故,或者什么原因,请求到达断点处就不运行了,等了一分多钟就自动退出App了,如果有大佬知道帮忙解决一下吧

image.png

既然在前端搞不定那就另辟蹊径,我们

新建一个springboot项目

EasyDL零门槛AI开发平台 - 图像分类API调用文档 | 百度AI开放平台 (baidu.com)

根据官网给出的请求工具类复制到项目util文件夹中

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;

/**
 * http 工具类
 */
public class HttpUtil {

    public static String post(String requestUrl, String accessToken, String params)
            throws Exception {
        String contentType = "application/x-www-form-urlencoded";
        return HttpUtil.post(requestUrl, accessToken, contentType, params);
    }

    public static String post(String requestUrl, String accessToken, String contentType, String params)
            throws Exception {
        String encoding = "UTF-8";
        if (requestUrl.contains("nlp")) {
            encoding = "GBK";
        }
        return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
    }

    public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
            throws Exception {
        String url = requestUrl + "?access_token=" + accessToken;
        return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
    }

    public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
            throws Exception {
        URL url = new URL(generalUrl);
        // 打开和URL之间的连接
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        // 设置通用的请求属性
        connection.setRequestProperty("Content-Type", contentType);
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setUseCaches(false);
        connection.setDoOutput(true);
        connection.setDoInput(true);

        // 得到请求的输出流对象
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        out.write(params.getBytes(encoding));
        out.flush();
        out.close();

        // 建立实际的连接
        connection.connect();
        // 获取所有响应头字段
        Map<String, List<String>> headers = connection.getHeaderFields();
        // 遍历所有的响应头字段
        for (String key : headers.keySet()) {
            System.err.println(key + "--->" + headers.get(key));
        }
        // 定义 BufferedReader输入流来读取URL的响应
        BufferedReader in = null;
        in = new BufferedReader(
                new InputStreamReader(connection.getInputStream(), encoding));
        String result = "";
        String getLine;
        while ((getLine = in.readLine()) != null) {
            result += getLine;
        }
        in.close();
        System.err.println("result:" + result);
        return result;
    }
}

请求方法

public static String easydlImageClassify() {
    // 请求url
    url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/detection/sunshiny";
    try {
        Map<String, Object> map = new HashMap<>();
        map.put("image", "sfasq35sadvsvqwr5q...");
        map.put("top_num", "5");

        String param = GsonUtils.toJson(map);

        // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
        accessToken = "24.b6ad522191c974103e454ebbe853d95a.2592000.1662008914.282335-26712770";

        String result = HttpUtil.post(url, accessToken, "application/json", param);
        System.out.println(result);
        return result;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

小小的写一个测试接口

@GetMapping("/test")
public String getTest(String base64){
    return easydlImageClassify();
}

image.png

这边可以看到接口数据是成功转出,文件编码传输不正确

跨域

前往前端还有一步就是跨域

  1. WebMvcConfigurer接口

  2.1 addInterceptors:拦截器

  2.2 addViewControllers:页面跳转

  2.3 addResourceHandlers:静态资源

  2.4 configureDefaultServletHandling:默认静态资源处理器

  2.5 configureViewResolvers:视图解析器

  2.6 configureContentNegotiation:配置内容裁决的一些参数

  2.7 addCorsMappings:跨域

  2.8 configureMessageConverters:信息转换器

package zyy.clude.zyycludeh;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成BeanDefinition和服务请求
@Configuration
public class WebServiceConfig implements WebMvcConfigurer {
    //拦截器
    @Bean
    WebServiceInterceptor getW(){
        return new WebServiceInterceptor();
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //添加映射路径
        registry.addMapping("/**")
                //设置放行哪些原始域   SpringBoot2.4.4下低版本使用.allowedOrigins("*")
                .allowedOrigins("*")
                //放行哪些请求方式
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                //.allowedMethods("*") //或者放行全部
                //放行哪些原始请求头部信息
                .allowedHeaders("*")
                //暴露哪些原始请求头部信息
                .exposedHeaders("*");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getW())
                .addPathPatterns("/**")
                .excludePathPatterns("/login","/Test");//传入无需token的链接
    }
}

可以看到Android这边是可以拿到数据的

image.png

接口完善传入base64编码和条数

import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import zyy.clude.zyycludeh.util.GsonUtils;
import zyy.clude.zyycludeh.util.HttpUtil;

import java.util.HashMap;
import java.util.Map;

@RestController
public class UserController {

    private static String url;
    private static String accessToken;
    @Data
    private static class test{
        String image;
        String top_num;
    } 
    
    @PostMapping("/test")
    public String getTest(@RequestBody test test){
        return easydlImageClassify(test);
    }

    public static String easydlImageClassify(test test) {
        // 请求url
        url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/detection/sunshiny";
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("image", test.image);
            map.put("top_num", "5");

            String param = GsonUtils.toJson(map);

            // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
            accessToken = "24.b6ad522191c974103e454ebbe853d95a.2592000.1662008914.282335-26712770";

            String result = HttpUtil.post(url, accessToken, "application/json", param);
            System.out.println(result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

可以看到也正常显示,因为图片上没有显示猫狗,所以图片也没有数据 image.png

可以去在线转换Base64编码进行测试,转出的码记得要去除头部信息 data:image/jpeg;base64,

image.png

postman中也是成功显示两只狗的坐标

image.png

最后

项目中一切从简,都需要重新封装,token等都需要从前端传入后端,获取token时间进行持久化 图片的优化等,如果有更好的意见欢迎到评论区讨论,尤其是那个问题