1、图像识别
<template>
<a-card class="contablelist" :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
</a-form>
</div>
<!-- 查询区域-END -->
<div class="contable">
<!-- 操作按钮区域 -->
<div class="table-operator">
<a-button @click="handleAdd" type="primary" class="xz" icon="plus">新增</a-button>
<a-button type="primary" icon="download" class="dc" @click="handleExportXls('模型绑定')">导出</a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary"class="dr" icon="import">导入</a-button>
</a-upload>
<!-- 高级查询区域 -->
<a-dropdown v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay">
<a-menu-item key="1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /></a-button>
</a-dropdown>
</div>
<!-- table区域-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a
style="font-weight: 600">{{ selectedRowKeys.length }}</a>项
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
<a-table ref="table" size="middle" :scroll="{x:true}" bordered rowKey="id" :columns="columns"
:dataSource="dataSource" :pagination="ipagination" :loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" class="j-table-force-nowrap"
@change="handleTableChange">
<template slot="htmlSlot" slot-scope="text">
<div v-html="text"></div>
</template>
<template slot="imgSlot" slot-scope="text,record">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无图片</span>
<img v-else :src="getImgView(text)" :preview="record.id" height="25px" alt=""
style="max-width:80px;font-size: 12px;font-style: italic;" />
</template>
<template slot="fileSlot" slot-scope="text">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" icon="download" size="small" @click="downloadFile(text)">
下载
</a-button>
</template>
<span slot="action" slot-scope="text, record">
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical" />
<a @click="handleIdentify(record)">AI识别</a>
<!-- <a-divider type="vertical" v-if="record.spaceOne==='1'" />
<a v-if="record.spaceOne==='1'" @click="handleIdentifyClose(record)">视频识别结束</a>
<a-divider type="vertical" v-if="record.spaceOne==='1'" />
<a v-if="record.spaceOne==='1'" @click="handleOpenVideo(record)">视频区域报警配置</a>-->
<a-divider type="vertical" />
<a-dropdown>
<a class="ant-dropdown-link">更多
<a-icon type="down" />
</a>
<a-menu slot="overlay">
<a-menu-item>
<a v-if="record.spaceOne==='1'" @click="handleOpenVideo(record)">视频区域报警配置</a>
</a-menu-item>
<a-menu-item>
<a v-if="record.spaceOne==='1'" @click="handleIdentifyClose(record)">视频识别结束</a>
</a-menu-item>
<a-menu-item>
<a @click="handleDetail(record)">详情</a>
</a-menu-item>
<a-menu-item>
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<a>删除</a>
</a-popconfirm>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
</a-table>
</div>
<tab-ai-model-bund-modal class="contc" :width="1200" ref="modalForm" @ok="modalFormOk"></tab-ai-model-bund-modal>
</div>
</a-card>
</template>
<script>
import {
httpAction,
getAction
} from '@/api/manage'
import '@/assets/less/TableExpand.less'
import {
mixinDevice
} from '@/utils/mixin'
import {
JeecgListMixin
} from '@/mixins/JeecgListMixin'
import TabAiModelBundModal from './modules/TabAiModelBundModal'
import { filterObj } from '@/utils/util';
export default {
name: 'TabAiModelBundList',
mixins: [JeecgListMixin, mixinDevice],
components: {
TabAiModelBundModal
},
data() {
return {
description: '模型绑定管理页面',
// 表头
columns: [{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: "center",
customRender: function(t, r, index) {
return parseInt(index) + 1;
}
},
{
title: '识别名称',
align: "center",
dataIndex: 'spaceTwo'
},
{
title: '模型名称',
align: "center",
dataIndex: 'modelName_dictText'
}, {
title: '识别类型',
align: "center",
dataIndex: 'spaceOne_dictText'
},
{
title: '图片|视频地址',
align: "center",
dataIndex: 'sendUrl'
},
{
title: '图片显示',
align: "center",
dataIndex: 'saveUrl',
scopedSlots: {
customRender: 'imgSlot'
}
},{
title:'是否推送',
align:"center",
dataIndex: 'isPush'
}, {
title:'是否播报',
align:"center",
dataIndex: 'isAudio'
}, {
title:'推送地址',
align:"center",
dataIndex: 'pushId_dictText'
}, {
title:'播报地址',
align:"center",
dataIndex: 'audioId_dictText'
},
{
title: '备注',
align: "center",
dataIndex: 'remake'
},
{
title: '操作',
dataIndex: 'action',
align: "center",
fixed: "right",
width: 200,
scopedSlots: {
customRender: 'action'
}
}
],
url: {
list: "/tab/tabAiModelBund/list",
delete: "/tab/tabAiModelBund/delete",
deleteBatch: "/tab/tabAiModelBund/deleteBatch",
exportXlsUrl: "/tab/tabAiModelBund/exportXls",
importExcelUrl: "tab/tabAiModelBund/importExcel",
identifyUrl: "/tab/tabAiHistory/addIdentify",
identifyCloseUrl: "/tab/tabAiHistory/addIdentifyClose"
},
dictOptions: {},
superFieldList: [],
}
},
created() {
this.getSuperFieldList();
},
computed: {
importExcelUrl: function() {
return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`;
},
},
methods: {
initDictConfig() {},
getSuperFieldList() {
let fieldList = [];
fieldList.push({
type: 'string',
value: 'modelName',
text: '模型名称',
dictCode: ''
})
fieldList.push({
type: 'string',
value: 'sendUrl',
text: '输入图片地址',
dictCode: ''
})
fieldList.push({
type: 'string',
value: 'saveUrl',
text: '保存图片地址',
dictCode: ''
})
fieldList.push({
type: 'string',
value: 'remake',
text: '备注',
dictCode: ''
})
this.superFieldList = fieldList
},
handleIdentify(info) {
console.log("info", this.url);
let that = this;
this.$confirm({
title: "确认识别吗",
content: "手动触发图片识别只会生成一次结果! 但视频会识别到结束",
onOk: function() {
let httpurl = '';
let method = '';
// debugger;
httpurl += that.url.identifyUrl;
method = 'post';
httpAction(httpurl, info, method).then((res) => {
if (res.success) {
that.$message.success(res.message);
that.$emit('ok');
} else {
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
})
}
});
},
handleIdentifyClose(info) {
let that = this;
this.$confirm({
title: "确认结束视频结束吗",
content: "结束视频识别结果输出!",
onOk: function() {
let httpurl = '';
let method = '';
// debugger;
httpurl += that.url.identifyCloseUrl;
method = 'post';
httpAction(httpurl, info, method).then((res) => {
if (res.success) {
that.$message.success(res.message);
that.$emit('ok');
} else {
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
})
}
});
},
handleOpenVideo(info){
this.$router.push('livecanvas/AddressList');
}
}
}
</script>
<style src="@assets/zwyStyle/css/main.css"></style>
<style scoped>
@import '~@assets/less/common.less';
/deep/ .ant-table-scroll{height: calc(100vh - 337px);}
</style>
前端就一个点击事件,主要是查看后端识别图片信息的功能。
@Override
public Result<String> startAi(TabAiModelBund tabAiModelBund, String path, String userId) {
TabAiModel aiModel=modelMapper.selectById(tabAiModelBund.getModelName());
if(aiModel!=null){
switch (aiModel.getSpareOne()){
case "1": //v3
{
log.info("【进入V3开始识别内容】{}",tabAiModelBund.getSpaceTwo());
if(tabAiModelBund.getSpaceOne().equals("0")){ //当前为图片
int a=this.saveIdentify(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
}else{
// 输出视频
// tabAiHistoryService.saveIdentifyVideo(tabAiModelBund,uploadpath);
// 输出坐标 延迟3-5s
//tabAiHistoryService.saveIdentifyLocalVideo(tabAiModelBund,uploadpath,sysUser.getId());
//多线程输出坐标
this.saveIdentifyLocalVideoThread(tabAiModelBund,path,userId);
return Result.OK("视频识别开始");
}
break;
}
case "2":
{
log.info("【进入V5开始识别内容】{}",tabAiModelBund.getSpaceTwo());
if(tabAiModelBund.getSpaceOne().equals("0")){ //当前为图片
if(tabAiModelBund.getSpaceTwo().indexOf("车牌")>-1||tabAiModelBund.getSpaceTwo().indexOf("区域")>-1){
log.info("【进入车牌识别内容】{}",tabAiModelBund.getSpaceTwo());
int a=this.saveCarIdentifyV5(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
}else{
int a=this.saveIdentifyYolov5(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
}
}else{
// 输出视频
// tabAiHistoryService.saveIdentifyVideo(tabAiModelBund,uploadpath);
// 输出坐标 延迟3-5s
//tabAiHistoryService.saveIdentifyLocalVideo(tabAiModelBund,uploadpath,sysUser.getId());
//多线程输出坐标
this.saveIdentifyLocalVideoThreadV5(tabAiModelBund,path,userId);
return Result.OK("视频识别开始");
}
break;
}//v5
case "3":
{ log.info("【进入V8开始识别内容】{}",tabAiModelBund.getSpaceTwo());
log.info("【进入V8开始识别内容】{}",tabAiModelBund.getSpaceTwo());
if(tabAiModelBund.getSpaceOne().equals("0")){ //当前为图片
int a=this.saveIdentifyYolov8(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
}else{
// 输出视频
// tabAiHistoryService.saveIdentifyVideo(tabAiModelBund,uploadpath);
// 输出坐标 延迟3-5s
//tabAiHistoryService.saveIdentifyLocalVideo(tabAiModelBund,uploadpath,sysUser.getId());
//多线程输出坐标
this.saveIdentifyLocalVideoThread(tabAiModelBund,path,userId);
return Result.OK("视频识别开始");
}
}//v8
case "4": { break;}//json
case "5": { break;}//other
case "6": { //cv
log.info("【进入cv开始识别内容】{}",tabAiModelBund.getSpaceTwo());
if(tabAiModelBund.getSpaceTwo().equals("车牌识别")){
int a=this.saveCarIdentify(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
}
break;
}//json
case "7":{
log.info("【进入OCR文字识别内容】{}",tabAiModelBund.getSpaceTwo());
int a=this.saveStr(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
break;
}
case "9":{
log.info("【进入音频内容】{}",tabAiModelBund.getSpaceTwo());
int a=this.saveAudioStr(tabAiModelBund,path);
if(a==0){
return Result.OK("识别图片成功!");
}
break;
}
}
}
return Result.error("识别失败未发现识别内容");
}
图像识别的方法:
/***
* AI模型嵌套模型
* 需要绝对路径
* 输入图片
*/
public static String SendPicYoloV3(String weight, String cfg, String names, String picUrl, String saveName, String uploadpath) throws Exception {
log.info(uploadpath);
Long a=System.currentTimeMillis();
// 加载类别名称
List<String> classNames = Files.readAllLines(Paths.get(uploadpath+ File.separator +names));
// 加载YOLOv3模型
log.info("cfg地址{}",uploadpath+ File.separator +cfg);
log.info("weight地址{}",uploadpath+ File.separator +weight);
Net net = Dnn.readNetFromDarknet(uploadpath+ File.separator +cfg, uploadpath+ File.separator +weight);
// net.setPreferableBackend(Dnn.DNN_BACKEND_CUDA);
// net.setPreferableBackend(Dnn.DNN_TARGET_CUDA);
net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
net.setPreferableTarget(Dnn.DNN_TARGET_CPU);
// 读取输入图像
log.info("图片地址{}",uploadpath+ File.separator +picUrl);
Mat image = Imgcodecs.imread(uploadpath+ File.separator +picUrl);
// 将图像传递给模型进行目标检测
Mat blob = Dnn.blobFromImage(image, 1.0 / 255, new Size(416, 416), new Scalar(0), true, false);
net.setInput(blob);
// 将图像传递给模型进行目标检测
List<Mat> result = new ArrayList<>();
List<String> outBlobNames = net.getUnconnectedOutLayersNames();
net.forward(result, outBlobNames);
// 处理检测结果
float confThreshold = 0.5f;
List<Rect2d> boundingBoxes = new ArrayList<>();
List<Float> confidences = new ArrayList<>();
List<Integer> classIds = new ArrayList<>();
for (Mat level : result) {
for (int i = 0; i < level.rows(); ++i) {
Mat row = level.row(i);
Mat scores = level.row(i).colRange(5, level.cols());
Core.MinMaxLocResult minMaxLocResult = Core.minMaxLoc(scores);
Point classIdPoint = minMaxLocResult.maxLoc;
double confidence = row.get(0, 4)[0];
if (confidence > confThreshold) {
// log.info("classIdPoint"+ classIdPoint);
// log.info("classIdPointx"+ classIdPoint.x);
classIds.add((int) classIdPoint.x); //记录标签下标
double centerX = row.get(0, 0)[0] * image.cols();
double centerY = row.get(0, 1)[0] * image.rows();
double width = row.get(0, 2)[0] * image.cols();
double height = row.get(0, 3)[0] * image.rows();
double left = centerX - width / 2;
double top = centerY - height / 2;
// 绘制边界框
Rect2d rect = new Rect2d(left, top, width, height);
boundingBoxes.add(rect);
confidences.add((float)confidence);
}
}
}
// 执行非最大抑制,消除重复的边界框
MatOfRect2d boxes = new MatOfRect2d(boundingBoxes.toArray(new Rect2d[0]));
MatOfFloat confidencesMat = new MatOfFloat();
confidencesMat.fromList(confidences);
MatOfInt indices = new MatOfInt();
Dnn.NMSBoxes(boxes, confidencesMat, confThreshold, 0.4f, indices);
if(indices.empty()){
log.info("类别下标啊"+"未识别到内容");
return "error";
}
int[] indicesArray= indices.toArray();
// 获取保留的边界框
log.info(confidences.size()+"类别下标啊"+indicesArray.length);
// 在图像上绘制保留的边界框
int c=0;
for (int idx : indicesArray) {
Rect2d box = boundingBoxes.get(idx);
System.out.println("绘制111111"+"x:"+box.x+"y:"+ box.y+"");
System.out.println("绘制11111111"+"width:"+box.width+"y:"+ box.height+"");
Imgproc.rectangle(image, new Point(box.x, box.y), new Point(box.x + box.width, box.y + box.height),CommonColors(c), 2);
// 添加类别标签
log.info("当前有多少"+confidences.get(idx));
Integer ab=classIds.get(idx);
log.info("类别下标"+ab);
// AIModelYolo3.addChineseText(image, caption,new Point(box.x, box.y - 5));
Imgproc.putText(image, classNames.get(ab), new Point(box.x, box.y - 5), Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, CommonColors(c), 1);
c++;
}
String savepath=uploadpath + File.separator + "temp" + File.separator;
if(StringUtils.isNotBlank(saveName)){
savepath+=saveName+".jpg";
}else{
saveName=System.currentTimeMillis()+"";
savepath+=saveName+".jpg";
}
log.info(savepath);
Imgcodecs.imwrite(savepath, image);
Long b=System.currentTimeMillis();
log.info("消耗时间:"+(b-a));
return saveName+".jpg";
}
主要调用opencv中的图像方法来识别图片的内容结果。
2、视频识别
待完善。。。。。。