最近心血来潮,搞一个垃圾分类玩玩
但是简单的垃圾分类怎么能满足我,我要加一点好玩的,刺激的,没玩过的~~
当然是图像识别啦,虽然我这个菜鸡没学过图像识别这种高难度的,但是我可以调用科大讯飞的接口,直接获取信息就OK啦
首先我找到了科大讯飞的图像识别api,一下就看到了物体识别!
物体识别,采用全球领先的通用物体检测算法,有效检测图像中的动物、交通工具、生活家具等2万多种生活常见物体。
该能力是通过HTTP API的方式给开发者提供一个通用的接口,适用于一次性交互数据传输的AI服务场景,块式传输。相较于SDK,API具有轻量、跨语言的特点,不过请注意该接口使用的HTTP API协议不支持跨域。
科大讯飞物体识别链接
一下子能识别2w多种物体,这一下子就满足我的需求了,所以我就仔细看了一下怎么调用它
参数
调用它的接口很复杂,规则一大堆,看的很烦,物体识别文档,所以我直接看它的demo怎么写
我这边是用的java,我就封装了自己的工具类跟对象,为我所用
二.上代码(后端)
封装的对象类
@Data
public class ImagesDO {
// 请求地址
private String URL = "http://tupapi.xfyun.cn/v1/currency";
// 应用ID
private String APPID = "5db8d5";
// 接口密钥
private String API_KEY = "456de4716e02bd589d450a";
// 图片名称
public String IMAGE_NAME ;
// 图片路径
private String PATH ;
}
- URL : 请求的地址,固定的为物体识别的请求地址
- APPID : 在你的科大讯飞创建应用里获取
- API_KEY : 在你的科大讯飞创建应用里获取
- IMAGE_NAME : 图片的名字(跟格式)
- PATH : 图片的本地路径
处理图片的工具类
package com.fehead.testproject.controller.utils;
import java.io.*;
/**
* 文件操作工具类
*/
public class FileUtil {
/**
* 读取文件内容为二进制数组
*
* @param filePath
* @return
* @throws IOException
*/
public static byte[] read(String filePath) throws IOException {
InputStream in = new FileInputStream(filePath);
byte[] data = inputStream2ByteArray(in);
in.close();
return data;
}
/**
* 流转二进制数组
*
* @param in
* @return
* @throws IOException
*/
private static byte[] inputStream2ByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n = 0;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
/**
* 保存文件
*
* @param filePath
* @param fileName
* @param content
*/
public static void save(String filePath, String fileName, byte[] content) {
try {
File filedir = new File(filePath);
if (!filedir.exists()) {
filedir.mkdirs();
}
File file = new File(filedir, fileName);
OutputStream os = new FileOutputStream(file);
os.write(content, 0, content.length);
os.flush();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
http请求工具类
package com.fehead.testproject.controller.utils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
/**
* Http Client 工具类
*/
public class HttpUtil {
/**
* 发送post请求
*
* @param url
* @param header
* @param body
* @return
*/
public static String doPost1(String url, Map<String, String> header, byte[] body) {
String result = "";
BufferedReader in = null;
try {
// 设置 url
URL realUrl = new URL(url);
URLConnection connection = realUrl.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
// 设置 header
for (String key : header.keySet()) {
httpURLConnection.setRequestProperty(key, header.get(key));
}
// 设置请求 body
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
httpURLConnection.setRequestProperty("Content-Type", "binary/octet-stream");
OutputStream out = httpURLConnection.getOutputStream();
out.write(body);
out.flush();
out.close();
if (HttpURLConnection.HTTP_OK != httpURLConnection.getResponseCode()) {
System.out.println("Http 请求失败,状态码:" + httpURLConnection.getResponseCode());
return null;
}
// 获取响应body
in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
return null;
}
return result;
}
}
三.调用接口
@PostMapping("/upload")
public List<LabelDO> identityAudit(HttpServletResponse response, HttpServletRequest request) throws IOException {
System.out.println("进入get方法!");
MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;
MultipartFile multipartFile = req.getFile("file"); //对应前端页面的name值
// String path = request.getSession().getServletContext().getRealPath("/image");
String realPath = "E:/image"; //这里选取本地e盘下的路径作为图片保存路径
File dir = new File(realPath);//若不存在这个路径,则创建这个文件夹
if (!dir.exists()) {
dir.mkdir();
}
//生成一个新的文件名fileName
String n = UUID.randomUUID().toString().substring(0, 11);
String picName = n + "." + "jpg";
String d = realPath+"/"+ picName;
File file = new File(realPath, picName);
multipartFile.transferTo(file);
.........未完
}
- 这里我用的serverlet,会返回一个DO类的list,封装返回的五组数据
- 这里我会把上传的图片重新统一命名,命名规则为图片路径+图片名称
- 最后写入真实路径的文件
这里获取的图片路径是因为,之后调用物体识别接口会需要图片路径
调用接口实现
Map<String, String> bulider=imageService.choseImage(picName,d);
byte[] imageByteArray = FileUtil.read(d); //图片的二进制形式
String result = HttpUtil.doPost1("http://tupapi.xfyun.cn/v1/currency", bulider, imageByteArray);
System.out.println("接口调用结果:" + result);
- 首先把图片的图片名,路径封装到map里面
- 并且调用之前写好的文件的工具类,把本地图片变成二进制的形式
- 最后调用http工具类,填好参数,第一个是参数就是物体识别的请求的接口地址
返回结果
{"code":0,"data":{"fileList":[{"label":161,"labels":[161,6385,1729,9545,15938],"name":"3efa1937-0b.jpg","rate":0.15478475391864777,"rates":[0.15478475391864777,0.11142700910568237,0.11099418252706528,0.09142182022333145,0.07964999973773956],"review":true}],"reviewCount":1,"topNStatistic":[{"count":1,"label":161}]},"desc":"success","sid":"tup00002c04@dx0a061118c73a1aba00"}
{
"code":"0",
"data":[
{
"label":19015,
"labels":[
19015,
18927,
18929,
698,
5588
],
"name":"img.jpg",
"rate":0.10702908039093018,
"rates":[
0.10702908039093018,
0.08567219227552414,
0.0592394582927227,
0.04257886856794357,
0.04108942672610283
],
"review":true,
"tag":"Local Image"
}
],
"desc":"success",
"sid":"tup00000005@ch2ee40efd592d6a6b00"
}
返回的字符串很复杂,但也算是成功了
- labels 代表识别的物体的序列,一共有五组,相当于给你了最接近的五个物体
- name 你上传图片的名字
- rates 代表各个识别物体的相似概率
…
四.处理数据
既然成功的调用了接口,那我们现在开始来处理数据了
首先,我们把他转化成json来处理数据,这里我们用到了阿里的FastJson来处理,
详情可看我之前的博客FastJson处理数据
这里直接上代码
JSONObject jsonObject = JSON.parseObject(result);
JSONArray jsonArray=jsonObject.getJSONObject("data").getJSONArray("fileList").getJSONObject(0).getJSONArray("labels");
返回的jsonArray数据如下,我们直接获取了labels里的数据了,因为我们需要返回识别物体的label,其他的暂时不需要.
这里一长串代码肯定是看不懂的,我们需要在后台处理数据!
首先来看一下官方给的返回数据识别的物体label2w个太多了,但是很容易处理的
先给它封装成list,后面要用
List<Integer> list = new ArrayList<>();//获取五个最接近的物体的label
for(Object jstr:jsonArray){
list.add(Integer.parseInt(String.valueOf(jstr)));
}
导入数据库
下载了之前的的表,我们来导入自己的数据库里,我用的是mysql,可以直接用excel表的格式导入数据库,利用navicat的可视界面导入向导就可以导入到数据库中
这样的导入成功了
Mybatis-Plus登场
mp作为非常好用的mybatis的增强版,非常的强,详细学习可以参照我之前写的Mybatis-Plus学习来学习一下
D
@Data
@TableName("images")
public class LabelDO {
private Integer id;
private String englishName;
private String name;
private String classes;
private Integer label;
}
dao层
package com.fehead.testproject.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fehead.testproject.dataobject.LabelDO;
public interface LabelDOMapper extends BaseMapper<LabelDO> {
}
service层
@Service
public class LabelServiceImpl {
@Autowired
private LabelDOMapper labelDOMapper;
public List<LabelDO> selectLabel(List<Integer> list){
List<LabelDO> labelDOS=new ArrayList<>();
for (Integer integer:list) {
QueryWrapper<LabelDO> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("label",integer);
LabelDO labelDO=labelDOMapper.selectOne(queryWrapper);
String[] result=labelDO.getName().split(",");
String[] result1=result[0].split(";");
labelDO.setName(result1[0]);
labelDOS.add(labelDO);
}
return labelDOS;
}
}
- 首先利用了mp自带的方法查询每一个label对应的DO
- String[] result=labelDO.getName().split(",") 由于数据库会查到打蛋器,打蛋机 或长者; 长辈; 元老,这样的词语不能方便我们查询,所以各个词语相似,我们只取第一个词,这里用到了字符串的split方法,取出","前的第一个词
- String[] result1=result[0].split(";") 我们再来筛选";",上一步筛选了",",现在我们来在上一步的基础下筛选,这样我们取出的字符串就符合我们的要求了
- 最终返回的是一个对象list
最后一步,调用service层的方法就可以啦,我太懒了就没写处理异常的,你们可以加
List<LabelDO> labelDOS=labelService.selectLabel(list);
return labelDOS;
我们来看一下最终返回的数据
五.前端部分(微信小程序)
这里用作演示,就简单的写一个小程序的前端看看,代码先放出来,下一篇博客好好做前端
差不多这种,凑活的看一下啦
页面的js
// pages/images/images.js
Page({
/**
* 页面的初始数据
*/
data: {
InputBottom: "",
imagesfindName:'',
garbageName:[],
tempFilePaths1:''
},
ViewImage(e) {
wx.previewImage({
urls: this.data.imgList,
current: e.currentTarget.dataset.url
});
},
ChooseImage() {
var that=this
wx.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], //从相册选择
success: (res) => {
console.log(res)
var tempFilePaths = res.tempFilePaths;
that.setData({
tempFilePaths1: tempFilePaths
})
// wx.showToast({
// title: '正在上传...',
// icon: 'loading',
// mask: true,
// duration: 10000
// })
// url: 'http://127.0.0.1:8091/images/upload', //仅为示例,非真实的接口地址
// http://api.choviwu.top/garbage/uploadFile
wx.uploadFile({
url: 'http://127.0.0.1:8091/images/upload', //仅为示例,非真实的接口地址
filePath: tempFilePaths[0],
name: 'file',
header: {
"Content-Type": "multipart/form-data",
'accept': 'application/json',
},
formData: {
},
success(res) {
var json = JSON.parse(res.data)
console.log(json)
that.setData({
imagesfindName: json
})
for(var i=0;i<that.data.imagesfindName.length;i++){
that.searchgarbage(that.data.imagesfindName[i]);
}
}
})
}
});
},
searchgarbage:function(e){
var that=this
wx.request({
url: 'https://service.xiaoyuan.net.cn/garbage/index/search',
method:"GET",
data:{
kw : e.name
},
success: function(res){
console.log(res.data.data[0])
that.setData({
garbageName: that.data.garbageName.concat(res.data.data[0])
})
}
})
},
InputFocus(e) {
this.setData({
InputBottom: e.detail.height
})
},
InputBlur(e) {
this.setData({
InputBottom: 0
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
wxml代码
<view>
<button bindtap="ChooseImage">选图片</button>
</view>
<view class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="bg-img" wx:for="{{tempFilePaths1}}" wx:key="{{index}}" bindtap="ViewImage" data-url="{{tempFilePaths1[index]}}">
<image src='{{tempFilePaths1[index]}}' mode='aspectFill' ></image>
<view class="cu-tag bg-red" catchtap="DelImg" data-index="{{index}}">
<text class="cuIcon-close"></text>
</view>
</view>
</view>
</view>
<view wx:for="{{garbageName}}">
<view>{{item.name}}------{{item.category}}</view>
</view>