|
@@ -25,17 +25,9 @@
|
|
|
<el-button type="primary" link icon="View"> 终止 </el-button>
|
|
|
</template>
|
|
|
</el-popconfirm>
|
|
|
- <el-button type="primary" link icon="View" @click="openResultDialog(scope.row.id)" v-if="scope.row.status == '2'"> 预览 </el-button>
|
|
|
+ <el-button type="primary" link icon="View" @click="compareDataAugmentation(scope.row)" v-if="scope.row.status == '2'"> 预览 </el-button>
|
|
|
<el-button type="primary" link icon="View" @click="downloadFile(scope.row)" v-if="scope.row.status == '2'"> 导出 </el-button>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- link
|
|
|
- icon="View"
|
|
|
- @click="openMetricDialog(scope.row.id)"
|
|
|
- v-if="(scope.row.taskType == '图像增强' || scope.row.taskType == '图像逆光') && scope.row.status == '2'"
|
|
|
- >
|
|
|
- 指标
|
|
|
- </el-button>
|
|
|
+ <el-button type="primary" link icon="View" @click="openMetricDialog(scope.row.id)" v-if="scope.row.status == '2'"> 指标 </el-button>
|
|
|
<el-button type="primary" link icon="View" v-auth="['demo:DataAugmentation:query']" @click="openDialog(3, '任务查看', scope.row)">
|
|
|
查看
|
|
|
</el-button>
|
|
@@ -56,7 +48,7 @@
|
|
|
</ProTable>
|
|
|
<DataAugmentationFormDialog ref="formDialogRef" />
|
|
|
<ImportExcel ref="dialogRef" />
|
|
|
- <el-dialog v-model="dialogVisible" title="结果预览" width="80%">
|
|
|
+ <!-- <el-dialog v-model="dialogVisible" title="结果预览" width="80%">
|
|
|
<div style="display: flex; text-align: center">
|
|
|
<div style="width: 50%" v-if="inputVideoUrl">
|
|
|
<video controls width="90%">
|
|
@@ -71,12 +63,28 @@
|
|
|
</video>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
- <!-- <VideoWindow ref="videoWindowRef" :video-url="url"></VideoWindow> -->
|
|
|
- <!-- <el-dialog v-model="logDialogVisible" title="日志" width="80%">
|
|
|
- <el-text class="mx-1">{{ logDialog }}</el-text>
|
|
|
</el-dialog> -->
|
|
|
+
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="80%" @close="handleClose">
|
|
|
+ <el-select v-model="imageFps" placeholder="选择帧率" style="width: 200px" @change="changeFps">
|
|
|
+ <el-option label="0" value="0"></el-option>
|
|
|
+ <el-option label="5" value="5"></el-option>
|
|
|
+ <el-option label="15" value="15"></el-option>
|
|
|
+ <el-option label="30" value="30"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <div class="image-dialog" v-if="imageIdx >= 0 && cacheImages[imageIdx]">
|
|
|
+ <div style="width: 50%">
|
|
|
+ <el-image :src="cacheImages[imageIdx].origin[0]"></el-image>
|
|
|
+ </div>
|
|
|
+ <div style="width: 50%">
|
|
|
+ <el-image :src="cacheImages[imageIdx].stable[0]"></el-image>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="image-dialog-btn" v-if="imageFps == 0">
|
|
|
+ <el-button type="primary" @click="pre_picture" :disabled="imageIdx <= 0">上一个</el-button>
|
|
|
+ <el-button type="primary" @click="next_picture" :disabled="imageIdx >= fileCount - 1">下一个</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
<ViewLog ref="viewLogRef" :get-log-api="getDialogApi" />
|
|
|
<el-dialog v-model="metricDialogVisible" title="算法结果指标" width="500px" style="text-align: center">
|
|
|
<el-table :data="metricList" style="width: 100%; margin-left: 10%; text-align: left">
|
|
@@ -88,7 +96,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="tsx" name="DataAugmentation">
|
|
|
-import { ref, reactive, onMounted, computed, onUnmounted, onBeforeMount } from 'vue'
|
|
|
+import { ref, reactive, onMounted, computed, onUnmounted, onBeforeMount, watch, nextTick } from 'vue'
|
|
|
import { useHandleData } from '@/hooks/useHandleData'
|
|
|
import { useDownload } from '@/hooks/useDownload'
|
|
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
|
@@ -96,7 +104,6 @@ import ProTable from '@/components/ProTable/index.vue'
|
|
|
import ImportExcel from '@/components/ImportExcel/index.vue'
|
|
|
import ViewLog from '@/views/demo/components/ViewLog.vue'
|
|
|
|
|
|
-import VideoWindow from '@/views/demo/components/VideoWindow.vue'
|
|
|
import DataAugmentationFormDialog from '@/components/DataAugmentationFormDialog/index.vue'
|
|
|
import { ProTableInstance, ColumnProps, EnumProps } from '@/components/ProTable/interface'
|
|
|
import {
|
|
@@ -117,13 +124,150 @@ import {
|
|
|
getMetricApi,
|
|
|
getVideoUrl
|
|
|
} from '@/api/modules/demo/dataAugmentation'
|
|
|
-// import { } from '@/api/modules/system/dictData'
|
|
|
+// import { VideoPlayer } from 'vue-video-player'
|
|
|
+// import 'vue-video-player/dist/simple.css'
|
|
|
+
|
|
|
+// const playerOptions = ref({})
|
|
|
+// const videoPlayer = ref<InstanceType<typeof VideoPlayer> | null>(null);
|
|
|
+
|
|
|
+// const playVideo = () => {
|
|
|
+// if (videoPlayer.value) {
|
|
|
+// videoPlayer.value.player.play();
|
|
|
+// }
|
|
|
+// };
|
|
|
+
|
|
|
+// const pauseVideo = () => {
|
|
|
+// if (videoPlayer.value) {
|
|
|
+// videoPlayer.value.player.pause();
|
|
|
+// }
|
|
|
+// };
|
|
|
|
|
|
-const videoWindowRef = ref()
|
|
|
-const playerOptions = ref({})
|
|
|
+// const onPlay = () => {
|
|
|
+// console.log('视频开始播放');
|
|
|
+// };
|
|
|
+
|
|
|
+// const onPause = () => {
|
|
|
+// console.log('视频暂停播放');
|
|
|
+// };
|
|
|
+// const setPlayerOptions = (url) => {
|
|
|
+
|
|
|
+// playerOptions.value = {
|
|
|
+// sources: [{
|
|
|
+// type: "video/mp4",
|
|
|
+// src: url
|
|
|
+// }],
|
|
|
+// poster: "", // 视频封面图
|
|
|
+// autoplay: false,
|
|
|
+// controls: true,
|
|
|
+// fluid: true // 响应式布局
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+const handleClose = () => {
|
|
|
+ if (intervalChangeFps.value) {
|
|
|
+ clearInterval(intervalChangeFps.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const changeFps = () => {
|
|
|
+ console.log('changeFps')
|
|
|
+ if (intervalChangeFps.value) {
|
|
|
+ clearInterval(intervalChangeFps.value)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (imageFps.value == 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ imageIdx.value = 1
|
|
|
+ intervalChangeFps.value = setInterval(() => {
|
|
|
+ next_picture()
|
|
|
+ }, 1000 / imageFps.value)
|
|
|
+}
|
|
|
+const dialogVisible = ref(false)
|
|
|
+const taskId = ref('')
|
|
|
+const imageIdx = ref(0)
|
|
|
+const imageBase64List = ref({
|
|
|
+ origin: '',
|
|
|
+ stable: ''
|
|
|
+})
|
|
|
+const inFileCount = ref(0)
|
|
|
+const outFileCount = ref(0)
|
|
|
+const cacheImages = ref({})
|
|
|
+const dialogTitle = ref('')
|
|
|
+const fileCount = ref(0)
|
|
|
+const imageFps = ref(0)
|
|
|
+const intervalChangeFps: any = ref()
|
|
|
+const loadImageData = async (taskId: string, imageIdx: number) => {
|
|
|
+ const res: any = await getCompareImageApi(taskId, imageIdx)
|
|
|
+ // imageBase64List.value.origin = res.origin
|
|
|
+ // imageBase64List.value.stable = res.stable
|
|
|
+ cacheImages.value[imageIdx].origin = res.origin
|
|
|
+ cacheImages.value[imageIdx].stable = res.stable
|
|
|
+}
|
|
|
+const task_type_view = ref('')
|
|
|
+const compareDataAugmentation = async (params: any) => {
|
|
|
+ if (taskId.value !== '' && taskId.value !== null && taskId.value !== undefined && taskId.value == params.id) {
|
|
|
+ dialogVisible.value = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+ task_type_view.value = params.taskType
|
|
|
+ taskId.value = params.id
|
|
|
+ imageIdx.value = 0
|
|
|
+ cacheImages.value = {}
|
|
|
+ const resCount: any = await getCompareImageCountApi(params.id)
|
|
|
+ if (resCount.code === 200) {
|
|
|
+ inFileCount.value = resCount.data.inFileCount
|
|
|
+ outFileCount.value = resCount.data.outFileCount
|
|
|
+ } else {
|
|
|
+ ElMessage.error('获取图片对比数量失败')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ dialogVisible.value = true
|
|
|
+
|
|
|
+ dialogTitle.value = '缓存图片中'
|
|
|
+ fileCount.value = Math.min(inFileCount.value, outFileCount.value)
|
|
|
+ const res: any = await getCompareImageApi(taskId.value)
|
|
|
+ for (let idx = 1; idx <= fileCount.value; idx++) {
|
|
|
+ dialogTitle.value = '缓存图片中: 第' + idx + '个样本 共' + fileCount.value + '个样本'
|
|
|
+ if (cacheImages.value[idx - 1]) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ //const res: any = await getCompareImageApi(taskId.value, idx - 1)
|
|
|
+ // console.log(res)
|
|
|
+
|
|
|
+ cacheImages.value[idx - 1] = {
|
|
|
+ origin: res.origin[idx - 1],
|
|
|
+ stable: res.stable[idx - 1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log(cacheImages.value[0])
|
|
|
+ if (fileCount.value > 0) {
|
|
|
+ dialogTitle.value = '预览: 第1个样本 共' + fileCount.value + '个样本'
|
|
|
+ } else {
|
|
|
+ dialogTitle.value = '无结果预览'
|
|
|
+ }
|
|
|
+
|
|
|
+ // next_picture()
|
|
|
+}
|
|
|
+const next_picture = async () => {
|
|
|
+ if (imageIdx.value < fileCount.value - 1) {
|
|
|
+ if (!cacheImages.value[imageIdx.value + 1]) {
|
|
|
+ await loadImageData(taskId.value, imageIdx.value + 1)
|
|
|
+ }
|
|
|
+ imageIdx.value = imageIdx.value + 1
|
|
|
+ }
|
|
|
+ dialogTitle.value = '预览: 第' + (imageIdx.value + 1) + '个样本 共' + fileCount.value + '个样本'
|
|
|
+}
|
|
|
+const pre_picture = async () => {
|
|
|
+ if (imageIdx.value > 0) {
|
|
|
+ if (!cacheImages.value[imageIdx.value - 1]) {
|
|
|
+ await loadImageData(taskId.value, imageIdx.value - 1)
|
|
|
+ }
|
|
|
+ imageIdx.value = imageIdx.value - 1
|
|
|
+ }
|
|
|
+ dialogTitle.value = '预览: 第' + (imageIdx.value + 1) + '个样本 共' + fileCount.value + '个样本'
|
|
|
+}
|
|
|
|
|
|
-const url = 'https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm'
|
|
|
-//const url = "https://v17.dogevideo.com/vcloud/17/v/20190424/1556036075_818c4125ec9c8cbc7a7a8a7cc1601512/1037/7d515b22c4958598c0fbd1e6290a5ca5.mp4?vkey=B561D3&tkey=17316085819fec852b94&auth_key=1731622981-omcPUa7d7Zy7svg9-1035588803-64c7aed2dc55c2766fcbb126aec1452a"
|
|
|
//打开指标窗口查看算法指标信息
|
|
|
const metricDialogVisible = ref(false)
|
|
|
const metricList = reactive([])
|
|
@@ -134,35 +278,37 @@ const openMetricDialog = async (id: string | number) => {
|
|
|
metricDialogVisible.value = true
|
|
|
}
|
|
|
|
|
|
-const dialogVisible = ref(false)
|
|
|
+// const dialogVisible = ref(false)
|
|
|
const inputVideoUrl = ref()
|
|
|
const outputVideoUrl = ref()
|
|
|
-const openResultDialog = async (id: string | number) => {
|
|
|
- const res: any = await getVideoUrl(id)
|
|
|
- inputVideoUrl.value = res.data.input
|
|
|
- outputVideoUrl.value = res.data.output
|
|
|
- console.log(inputVideoUrl)
|
|
|
- dialogVisible.value = true
|
|
|
-}
|
|
|
-const imageIdx = ref(0)
|
|
|
+// const openResultDialog = async (id: string | number) => {
|
|
|
+// const res: any = await getVideoUrl(id)
|
|
|
+// inputVideoUrl.value = res.data.input
|
|
|
+// outputVideoUrl.value = res.data.output
|
|
|
+// //setPlayerOptions(inputVideoUrl.value)
|
|
|
+// // inputVideoUrl.value = '/api/public/video/input/' + id
|
|
|
+// // outputVideoUrl.value = '/api/public/video/output/' + id
|
|
|
+// console.log(inputVideoUrl)
|
|
|
+// dialogVisible.value = true
|
|
|
+// }
|
|
|
|
|
|
const taskType = ref([])
|
|
|
const modelType = ref([
|
|
|
{
|
|
|
- label: 'yolov8_best',
|
|
|
- value: 'yolov8_best'
|
|
|
+ label: 'yolov8_best.pt',
|
|
|
+ value: 'yolov8_best.pt'
|
|
|
},
|
|
|
{
|
|
|
- label: 'yolov8_best_new',
|
|
|
- value: 'yolov8_best_new'
|
|
|
+ label: 'yolov8_best_new.pt',
|
|
|
+ value: 'yolov8_best_new.pt'
|
|
|
},
|
|
|
{
|
|
|
- label: 'yolov8__best',
|
|
|
- value: 'yolov8__best'
|
|
|
+ label: 'yolov8__best.pt',
|
|
|
+ value: 'yolov8__best.pt'
|
|
|
},
|
|
|
{
|
|
|
- label: 'yolov8__best_new',
|
|
|
- value: 'yolov8__best_new'
|
|
|
+ label: 'yolov8__best_new.pt',
|
|
|
+ value: 'yolov8__best_new.pt'
|
|
|
}
|
|
|
])
|
|
|
const trackingMethod = ref([
|
|
@@ -524,17 +670,64 @@ const addParams = (params, task_Type) => {
|
|
|
const obj: { [key: string]: number } = JSON.parse(validJsonString)
|
|
|
Object.keys(obj).forEach(key => {
|
|
|
model.value[key] = obj[key]
|
|
|
-
|
|
|
- itemsOptions.push({
|
|
|
- label: key,
|
|
|
- prop: key,
|
|
|
- rules: [{ trigger: 'blur' }],
|
|
|
- compOptions: {
|
|
|
- type: 'input',
|
|
|
- clearable: true,
|
|
|
- placeholder: '默认值为' + obj[key]
|
|
|
- }
|
|
|
- })
|
|
|
+ if (key === 'conf' || key === 'iou') {
|
|
|
+ itemsOptions.push({
|
|
|
+ label: key,
|
|
|
+ prop: key,
|
|
|
+ rules: [
|
|
|
+ {
|
|
|
+ trigger: 'change',
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (value === '' || value === undefined) {
|
|
|
+ return callback()
|
|
|
+ }
|
|
|
+ if (!value) {
|
|
|
+ return callback(new Error('请输入一个0到1之间的浮点数'))
|
|
|
+ }
|
|
|
+ const regex = /^(0(\.\d+)?|1(\.0+)?)$/
|
|
|
+ if (!regex.test(value)) {
|
|
|
+ return callback(new Error('请输入一个0到1之间的浮点数'))
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ compOptions: {
|
|
|
+ type: 'input',
|
|
|
+ clearable: true,
|
|
|
+ placeholder: '默认值为' + obj[key]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else if (key === 'imgsz') {
|
|
|
+ itemsOptions.push({
|
|
|
+ label: key,
|
|
|
+ prop: key,
|
|
|
+ rules: [
|
|
|
+ {
|
|
|
+ trigger: 'change',
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ //console.log(value)
|
|
|
+ if (value === '' || value === undefined) {
|
|
|
+ return callback()
|
|
|
+ }
|
|
|
+ if (!value) {
|
|
|
+ return callback(new Error('请输入一个大于0的整数'))
|
|
|
+ }
|
|
|
+ const regex = /^[1-9]\d*$/
|
|
|
+ if (!regex.test(value)) {
|
|
|
+ return callback(new Error('请输入一个大于0的整数'))
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ compOptions: {
|
|
|
+ type: 'input',
|
|
|
+ clearable: true,
|
|
|
+ placeholder: '默认值为' + obj[key]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|
|
|
} catch (error) {
|
|
|
console.error('解析 JSON 字符串时出错:', error)
|