【阿里云高校计划】基于阿里云表情、场景识别的电子相册搭建 day3 【一见如故】
【阿里云高校计划】基于阿里云表情、场景识别的电子相册搭建 day3 【一见如故】
电子相册搭建(表情、场景识别)
一、项目介绍
通过三个部分,实现上传照片、已上传照片轮播、照片根据表情和场景分类。本次算法进行了表情识别和场景识别,这些都是对阿里云的外部api调用而实现。
通过点击照片可以查看该照片的分类,通过点击已知分类可以查看该分类下的照片,从而达到一个电子相册的功能。
二、新建项目
1.新建spring boot项目
2.目录结构
- Application:启动的入口函数,
- common:一般就是存放公共的类或者常量,或者枚举值
- config:装载或者是数据库的配置,我们都会放在 config 下面
- WebAppConfig 是对我们的静态资源,比如说 css、js 还有一些图片,做了一个映射,比如说 static,然后我们把它映射到 classpath:/static 目录下
- Controller:我们接收外部的请求,比如参数校验,之后通过接口调用或得到的数据返回给前端
- Service: 把不同的请求 , 不同的服务把它抽象成一个 service,本项目有两个service,也可以认为是两个模块
- Utils: 存放公共的类或者工具函数
三、项目实现
(一)前端index.html
前端的实现是基于vue网页框架实现的,下面是scriipt.js中的一个例子
let customEvent = new Vue();//定义一个空的Vue实例
let tagIns = new Vue({
el: "#my-custom-tags",
data() {
return {
"tags": {}
}
},
beforeMount() {
this.getTags();
},
mounted() {
customEvent.$on('refreshTag',success => {
this.getTags();//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
})
},
methods: {
getBgcNo: function() {
return Math.ceil(Math.random() * 10) - 1
},
getTags: async function() {
let res = await axios.get("http://127.0.0.1:8080/album/v1/allCates")
this.tags = res.data
},
}
});
(二)控制层AlbumController
控制层主要包括下面几个部分:
- visionService、resourceService两个私有成员
- getList
- getCates
- getPhotosByCateAndLabel
- getPhotosByCate
- upload
(1)upload
为了避免重名,将文件名转换为MD5值,并返回,里面调用了Md5CaculateUtil
@PostMapping("/upload")
public Object upload(@RequestParam("file") MultipartFile file) throws Exception {
//计算上传文件的md5值,作为文件名
byte[] bytes = file.getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
String md5Str = Md5CaculateUtil.getMD5(inputStream);
inputStream.reset();
inputStream.mark(0);
String fileName = file.getOriginalFilename();
String fType = fileName.substring(fileName.lastIndexOf("."));
fileName = String.format("%s%s", md5Str, fType);
resourceService.saveAndRecognizeImage(fileName, inputStream);
return fileName;
}
(2)getList
获取全部图片,里面调用了getAllPhotos方法
@RequestMapping(value = "/list", method = RequestMethod.GET)
public Object getList() throws Exception {
return resourceService.getAllPhotos();
}
(3)getCates
获取分类,里面调用了getAllCates方法
@RequestMapping(value = "/allCates", method = RequestMethod.GET)
public Object getCates() throws Exception {
return resourceService.getAllCates();
}
(4)getPhotosByCateAndLabel
通过分类和标签获取图片
@RequestMapping(value = "/getPhotosByCateAndLabel", method = RequestMethod.GET)
public Object getPhotosByCateAndLabel(@RequestParam(name="cate") String cate, @RequestParam(name="tag") String tag) throws Exception {
return resourceService.getPhotosByCateAndLabel(cate, tag);
}
(5)getPhotosByCate
通过分类获取图片
@RequestMapping(value = "/getPhotosByCate", method = RequestMethod.GET)
public Object getPhotosByCate(@RequestParam(name="cate") String cate) throws Exception {
return resourceService.getPhotosByCate(cate);
}
(三)ResourceService资源管理器
主要包含以下几个部分:
- ResourceService类:
- 五个私有成员
- loadMetaData
- saveMetaData
- getPhotosByCateAndLabel
- getPhotosByCate
- getAllPhotos
- getAllCates
- getAccessPath
- saveAndRecognizeImage
- LabelModel类:
- 五个私有成员和三个静态属性
- LabelModel
- getAllImg
- getImgByCate
- getImgByCateAndLabel
- addImg
- removeImg
(一)ResourceService类
public class ResourceService {
private String scene;
@Value("${data.storage.path}")
private String storagePath;
@Value("${img.storage.path}")
private String imagePath;
static final String fileName = "data.json";
private LabelModel labelModel;
@Autowired
private VisionService visionService;
@PostConstruct
public void loadMetaData() {
log.info("load");
try {
InputStream inputStream = new FileInputStream(storagePath + fileName);
labelModel = JSONObject.parseObject(inputStream, LabelModel.class);
} catch (IOException e) {
log.error(e.toString());
labelModel = new LabelModel();
}
}
@PreDestroy
public void saveMetaData() {
log.info("save");
try {
System.out.println(JSON.toJSONString(labelModel));
OutputStream outputStream = new FileOutputStream(storagePath + fileName);
outputStream.write(JSON.toJSONBytes(labelModel));
outputStream.close();
} catch (Exception e) {
log.error(e.toString());
}
}
public List<String> getPhotosByCateAndLabel(String cate, String label) {
return getAccessPath(labelModel.getImgByCateAndLabel(cate, label));
}
public List<String> getPhotosByCate(String cate) {
return getAccessPath(labelModel.getImgByCate(cate));
}
public List<String> getAllPhotos() {
return getAccessPath(labelModel.getAllImg());
}
public Object getAllCates() {
return labelModel.cateMap;
}
private List<String> getAccessPath(List<String> imgs) {
List<String> result = new ArrayList<>();
imgs.stream().forEach(img-> {
result.add(String.format("/img/%s", img));
});
return result;
}
public void saveAndRecognizeImage(String filename, InputStream inputStream) {
log.info("saveImage");
try {
//识别场景
inputStream.reset();
inputStream.mark(0);
List<String> scenes = visionService.recognizeScene(inputStream);
labelModel.addImg(LabelModel.SCENE, filename, scenes);
inputStream.reset();
inputStream.mark(0);
List<String> expressions = visionService.recognizeExpression(inputStream);
labelModel.addImg(LabelModel.EXPRESSION, filename, expressions);
//重复使用InputStream需要reset、mark操作
inputStream.reset();
inputStream.mark(0);
//保存文件
System.out.println(imagePath+filename);
OutputStream outputStream = new FileOutputStream(imagePath + filename);
IOUtils.copy(inputStream, outputStream);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
log.error(e.toString());
}
}
(二)LabelModel类
@Data
static class LabelModel {
static final String SCENE = "scene";
static final String EXPRESSION = "expression";
static final String STYLE = "style";
//保存不同场景标签包含的图片
private Map<String, Set<String>> sceneMap;
//保存不同表情表情包含的图片
private Map<String, Set<String>> expressionMap;
//保存不同风格包含的图片
private Map<String, Set<String>> styleMap;
private Map<String, Set<String>> cateMap;
//图片中包含的标签
//key:图片地址
//value: Set中value值为: {[scene|expression|style]}_{label},例如:style_怀旧
private Map<String, Set<String>> imgLabels;
public LabelModel(){
this.imgLabels = new HashMap<>();
this.sceneMap = new HashMap<>();
this.expressionMap = new HashMap<>();
this.styleMap = new HashMap<>();
this.cateMap = new HashMap<>();
}
public List<String> getAllImg() {
List<String> result = new ArrayList<>();
imgLabels.forEach((k, v)-> {
result.add(k);
});
return result.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
}
public List<String> getImgByCate(String cate) {
Map<String, Set<String>> data = new HashMap<>();
switch (cate) {
case SCENE: {
data = sceneMap;
break;
}
case EXPRESSION: {
data = expressionMap;
break;
}
case STYLE: {
data = styleMap;
break;
}
}
Set<String> result = new HashSet<>();
data.forEach((k, d)-> {
result.addAll(d);
});
return result.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
}
public List<String> getImgByCateAndLabel(String cate, String label) {
System.out.println(cate);
System.out.println(label);
String key = String.format("%s_%s", cate, label);
Set<String> result = new HashSet<>();
switch (cate) {
case SCENE: {
result = sceneMap.get(key);
break;
}
case EXPRESSION: {
result = expressionMap.get(key);
break;
}
case STYLE: {
result = styleMap.get(key);
break;
}
}
return result.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
}
void addImg(String cate, String img, List<String> labels) {
for (String label : labels) {
String item = String.format("%s_%s", cate, label);
Set<String> imgSet = imgLabels.getOrDefault(img, new HashSet<>());
imgSet.add(item);
imgLabels.put(img, imgSet);
switch (cate) {
case SCENE: {
Set<String> sceneSet = sceneMap.getOrDefault(item, new HashSet<>());
sceneSet.add(img);
sceneMap.put(item, sceneSet);
Set<String> cateSet = cateMap.getOrDefault(cate, new HashSet<>());
cateSet.add(label);
cateMap.put(cate, cateSet);
break;
}
case EXPRESSION: {
Set<String> expressionSet = expressionMap.getOrDefault(item, new HashSet<>());
expressionSet.add(img);
expressionMap.put(item, expressionSet);
Set<String> cateSet = cateMap.getOrDefault(cate, new HashSet<>());
cateSet.add(label);
cateMap.put(cate, cateSet);
break;
}
case STYLE: {
Set<String> styleSet = styleMap.getOrDefault(item, new HashSet<>());
styleSet.add(img);
styleMap.put(item, styleSet);
Set<String> cateSet = cateMap.getOrDefault(cate, new HashSet<>());
cateSet.add(label);
cateMap.put(cate, cateSet);
break;
}
}
}
}
void removeImg(String img) {
Set<String> labels = imgLabels.remove(img);
labels.stream().forEach(label -> {
String[] segs = label.split("_");
String cate = segs[0];
String labelKey = segs[1];
switch (cate) {
case SCENE: {
sceneMap.get(labelKey).remove(img);
break;
}
case EXPRESSION: {
expressionMap.get(labelKey).remove(img);
break;
}
case STYLE: {
styleMap.get(labelKey).remove(img);
break;
}
}
});
}
}
加入高校计划
源代码在改了配置和地址的情况下,可以跑起来,但是网页控制台会报错。
同时也传入图片也没有响应。
今天已经是第三天,自己的技术还是不够,在前后端结合的地方还得抓紧学习。
源示例代码如下:
https://github.com/aliyun/alibabacloud-viapi-demo
加入高校计划
本人是参加的达摩院特别版-视觉AI训练营第二期
训练营里面的小哥哥小姐姐说话超级好听,也有超多大佬,我超喜欢这里!