AI平台对接
EasyDL从2017年11月中旬起,在国内率先推出针对AI零算法基础或者追求高效率开发的企业用户的零门槛AI开发平台,提供从数据采集、标注、清洗到模型训练、部署的一站式AI开发能力。对于各行各业有定制AI需求的企业用户来说,无论您是否具备AI基础,EasyDL设计简约,极易理解,最快5分钟即可上手学会,15分钟完成模型训练。
该平台还提供大量的AI应用场景
今天对接的功能是EasyDL图像中的物品检测
数据创建标注
进入物品检测平台后建造数据模型
创建完成之后进行图片导入,我们要检测的是猫狗杯子,所以将图片选择合适的方式进行导入
对图片进行标注每个种类标注几十张图片,然后交给平台智能标注即可
将数据标注完成之后,去我的模型中创建模型,选择数据集进行训练
训练完成就去发布模型,就可以将模型部署出来啦
经过一系列的操作之后发布出来的API就可以供我们使用了
使用EasySDL平台的API
根据平台指引拿到token
服务器返回的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
布局
拍照与相册代码
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了,如果有大佬知道帮忙解决一下吧
既然在前端搞不定那就另辟蹊径,我们
新建一个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();
}
这边可以看到接口数据是成功转出,文件编码传输不正确
跨域
前往前端还有一步就是跨域
- 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这边是可以拿到数据的
接口完善传入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;
}
}
可以看到也正常显示,因为图片上没有显示猫狗,所以图片也没有数据
可以去在线转换Base64编码进行测试,转出的码记得要去除头部信息 data:image/jpeg;base64,
postman中也是成功显示两只狗的坐标
最后
项目中一切从简,都需要重新封装,token等都需要从前端传入后端,获取token时间进行持久化 图片的优化等,如果有更好的意见欢迎到评论区讨论,尤其是那个问题