28968 7 сар өмнө
parent
commit
fea87477cb

+ 8 - 1
src/api/modules/demo/dataAugmentation.ts

@@ -33,7 +33,14 @@ export const listDataAugmentationApi1 = (query: DataAugmentationQuery) => {
 export const listDataAugmentationApi2 = (query: DataAugmentationQuery) => {
   return http.get<DataAugmentationVO[]>('/demo/dataAugmentation/targetDamageAcessList', query, { loading: false })
 }
-
+/**
+ * @name 查询目标追踪
+ * @param query 参数
+ * @returns 返回列表
+ */
+export const listDataAugmentationApi3 = (query: DataAugmentationQuery) => {
+  return http.get<DataAugmentationVO[]>('/demo/dataAugmentation/targetTrackList', query, { loading: false })
+}
 /**
  * @name 查询视频去抖动详细
  * @param id id

+ 2 - 1
src/components/DataAugmentationFormDialog/index.vue

@@ -132,7 +132,7 @@ const handleSubmit = () => {
         // data = { ...formModel, ...parameter.value.model }
         data = mergeWithCondition(formModel, parameter.value.model)
       }
-      let excludedKeys = ['name', 'taskType', 'inputOssId', 'remarks', 'modelAddress']
+      let excludedKeys = ['name', 'taskType', 'inputOssId', 'remarks', 'modelAddress', 'yolo_model', 'tracking_method']
       for (let key in data) {
         if (data.hasOwnProperty(key) && !excludedKeys.includes(key)) {
           if (!isFloat(data[key])) {
@@ -149,6 +149,7 @@ const handleSubmit = () => {
           }
         }
       }
+      excludedKeys = ['name', 'taskType', 'inputOssId', 'remarks', 'modelAddress']
       // 使用 Object.fromEntries 从指定的键值对创建新的对象
       let hyperparameters = Object.fromEntries(Object.entries(data).filter(([key, _]) => !excludedKeys.includes(key)))
       // 将 hyperparameters 对象转换为 JSON 字符串

+ 558 - 0
src/views/demo/targetTrack/index.vue

@@ -0,0 +1,558 @@
+<template>
+  <div class="table-box">
+    <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listDataAugmentationApi3">
+      <!-- 表格 header 按钮 -->
+      <template #tableHeader="scope">
+        <el-button type="primary" v-auth="['demo:DataAugmentation:add']" icon="CirclePlus" @click="openDialog(1, '任务新增')"> 新增 </el-button>
+        <!-- <el-button type="primary" v-auth="['demo:DataAugmentation:import']" icon="Upload" plain @click="batchAdd"> 导入</el-button> -->
+        <!-- <el-button type="primary" v-auth="['demo:DataAugmentation:export']" icon="Download" plain @click="downloadFile"> 导出 </el-button> -->
+        <el-button
+          type="danger"
+          v-auth="['demo:DataAugmentation:remove']"
+          icon="Delete"
+          plain
+          :disabled="!scope.isSelected"
+          @click="batchDelete(scope.selectedListIds)"
+        >
+          批量删除
+        </el-button>
+      </template>
+      <!-- 表格操作 -->
+      <template #operation="scope">
+        <el-button type="primary" link icon="View" @click="startDataAugmentation(scope.row)" v-if="scope.row.status == '0'"> 开始 </el-button>
+        <el-popconfirm title="确定终止此任务吗?" @confirm="stopDataAugmentation(scope.row)" v-if="scope.row.status == '1'">
+          <template #reference>
+            <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="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" v-auth="['demo:DataAugmentation:query']" @click="openDialog(3, '任务查看', scope.row)">
+          查看
+        </el-button>
+        <el-button
+          type="primary"
+          link
+          icon="View"
+          v-auth="['demo:DataAugmentation:query']"
+          @click="viewLogRef.handleOpen(scope.row.id)"
+          v-if="scope.row.status != '0' && scope.row.status != '1'"
+        >
+          日志
+        </el-button>
+        <el-button type="primary" link icon="Delete" v-auth="['demo:DataAugmentation:remove']" @click="deleteDataAugmentation(scope.row)">
+          删除
+        </el-button>
+      </template>
+    </ProTable>
+    <DataAugmentationFormDialog ref="formDialogRef" />
+    <ImportExcel ref="dialogRef" />
+    <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%">
+            <source :src="inputVideoUrl" />
+            Your browser does not support the video tag.
+          </video>
+        </div>
+        <div style="width: 50%" v-if="outputVideoUrl">
+          <video controls width="90%">
+            <source :src="outputVideoUrl" />
+            Your browser does not support the video tag.
+          </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> -->
+    <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">
+        <el-table-column prop="name" label="name" width="200px" />
+        <el-table-column prop="value" label="value" width="200px" />
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="tsx" name="DataAugmentation">
+import { ref, reactive, onMounted, computed, onUnmounted, onBeforeMount } from 'vue'
+import { useHandleData } from '@/hooks/useHandleData'
+import { useDownload } from '@/hooks/useDownload'
+import { ElMessageBox, ElMessage } from 'element-plus'
+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 {
+  listDataAugmentationApi3,
+  delDataAugmentationApi,
+  addDataAugmentationApi,
+  updateDataAugmentationApi,
+  importTemplateApi,
+  importDataAugmentationDataApi,
+  exportDataAugmentationApi,
+  getDataAugmentationApi,
+  startDataAugmentationApi,
+  stopDataAugmentationApi,
+  getCompareImageApi,
+  getCompareImageCountApi,
+  getDialogApi,
+  getTaskDictData,
+  getMetricApi
+} from '@/api/modules/demo/dataAugmentation'
+// import {  } from '@/api/modules/system/dictData'
+
+const videoWindowRef = ref()
+const playerOptions = ref({})
+
+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([])
+const openMetricDialog = async (id: string | number) => {
+  const res: any = await getMetricApi(id)
+  console.log(res.data)
+  metricList.splice(0, metricList.length, ...res.data)
+  metricDialogVisible.value = true
+}
+
+const dialogVisible = ref(false)
+const inputVideoUrl = ref()
+const outputVideoUrl = ref()
+const openResultDialog = (id: string | number) => {
+  inputVideoUrl.value = '/api/profile/upload/2024/11/17/schoolgirls_20241117233329A001_input/schoolgirls.mp4'
+  outputVideoUrl.value = '/api/profile/upload/2024/11/17/schoolgirls_20241117233329A001_input/schoolgirls.mp4'
+  //console.log(inputVideoUrl)
+  dialogVisible.value = true
+}
+const imageIdx = ref(0)
+
+const taskType = ref([])
+const modelType = ref([
+  {
+    label: 'yolov8_best',
+    value: 'yolov8_best'
+  },
+  {
+    label: 'yolov8_best_new',
+    value: 'yolov8_best_new'
+  },
+  {
+    label: 'yolov8__best',
+    value: 'yolov8__best'
+  },
+  {
+    label: 'yolov8__best_new',
+    value: 'yolov8__best_new'
+  }
+])
+const trackingMethod = ref([
+  {
+    label: 'botsort',
+    value: 'botsort'
+  },
+  {
+    label: 'strongsort',
+    value: 'strongsort'
+  },
+  {
+    label: 'bytetrack',
+    value: 'bytetrack'
+  },
+  {
+    label: 'ocsort',
+    value: 'ocsort'
+  },
+  {
+    label: 'imprassoc',
+    value: 'imprassoc'
+  },
+  {
+    label: 'deepocsort',
+    value: 'deepocsort'
+  }
+])
+const taskTypeEnums: EnumProps[] = []
+
+const hyperparameterConfiguration = []
+
+const viewLogRef = ref()
+
+const getTaskType = async () => {
+  const res: any = await getTaskDictData()
+  if (res.data.length != 0) {
+    taskType.value = res.data
+      .map(item => {
+        if (item.taskType === '多目标跟踪') {
+          return {
+            label: item.taskType,
+            value: item.taskType
+          }
+        } else {
+          return null
+        }
+      })
+      .filter(item => item !== null)
+    res.data.forEach(item => {
+      if (item.taskType === '多目标跟踪') {
+        taskTypeEnums.push({
+          label: item.taskType,
+          value: item.taskType,
+          disabled: false,
+          tagType: 'default'
+        })
+        let obj = {}
+        obj[item.taskType] = item.hyperparameterConfiguration
+        hyperparameterConfiguration.push(obj)
+      }
+    })
+  }
+}
+let intervalId: NodeJS.Timeout | null = null
+
+onUnmounted(() => {
+  // 组件卸载时清除定时器
+  if (intervalId !== null) {
+    clearInterval(intervalId)
+  }
+})
+
+onMounted(() => {
+  intervalId = setInterval(async () => {
+    proTable.value?.getTableList()
+  }, 5000)
+  getTaskType()
+})
+
+const startDataAugmentation = async (params: any) => {
+  const res = await startDataAugmentationApi(params.id)
+  if (res.code === 200) {
+    ElMessage.success('任务已经开始,请等待')
+  } else {
+    ElMessage.error('任务开始失败,请检查!')
+  }
+  proTable.value?.getTableList()
+}
+
+const stopDataAugmentation = async (params: any) => {
+  const res = await stopDataAugmentationApi(params.id)
+  if (res.code === 200) {
+    ElMessage.success('任务终止成功')
+  } else {
+    ElMessage.error('任务终止失败!')
+  }
+  proTable.value?.getTableList()
+}
+
+// ProTable 实例
+const proTable = ref<ProTableInstance>()
+
+// 删除视频去抖动信息
+const deleteDataAugmentation = async (params: any) => {
+  await useHandleData(delDataAugmentationApi, params.id, '删除任务【' + params.name + '】?')
+  proTable.value?.getTableList()
+}
+
+// 批量删除视频去抖动信息
+const batchDelete = async (ids: string[]) => {
+  await useHandleData(delDataAugmentationApi, ids, '删除所选任务?')
+  proTable.value?.clearSelection()
+  proTable.value?.getTableList()
+}
+
+// 导出视频去抖动列表
+const downloadFile = async task => {
+  ElMessageBox.confirm('确认导出任务数据?', '温馨提示', { type: 'warning' }).then(() =>
+    useDownload(exportDataAugmentationApi, 'export', task.outputPath)
+  )
+}
+
+// 批量添加视频去抖动
+const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
+
+const formDialogRef = ref<InstanceType<typeof DataAugmentationFormDialog> | null>(null)
+// 打开弹框的功能
+const openDialog = async (type: number, title: string, row?: any) => {
+  // hyperparameter.value = ''
+  let res = { data: {} }
+  if (row?.id) {
+    res = await getDataAugmentationApi(row?.id || null)
+    // hyperparameter.value = res.data?.hyperparameterConfiguration
+  }
+  // console.log(itemsOptions[1].compOptions?.value)
+  // 重置表单
+  // setItemsOptions()
+  const params = {
+    title,
+    width: 580,
+    isEdit: type !== 3,
+    itemsOptions: itemsOptions,
+    model: type == 1 ? model : res.data,
+    api: type == 1 ? addDataAugmentationApi : updateDataAugmentationApi,
+    getTableList: proTable.value?.getTableList
+  }
+  formDialogRef.value?.openDialog(params)
+}
+
+const statusEnums: EnumProps[] = [
+  {
+    label: '未开始',
+    value: '0',
+    disabled: false,
+    tagType: 'default'
+  },
+  {
+    label: '进行中',
+    value: '1',
+    disabled: false,
+    tagType: 'primary'
+  },
+  {
+    label: '完成',
+    value: '2',
+    disabled: false,
+    tagType: 'success'
+  },
+  {
+    label: '失败',
+    value: '3',
+    disabled: false,
+    tagType: 'danger'
+  },
+  {
+    label: '已中断',
+    value: '4',
+    disabled: false,
+    tagType: 'default'
+  }
+]
+
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  { type: 'selection', fixed: 'left', width: 70 },
+  { prop: 'id', label: '主键ID', width: 180 },
+  {
+    prop: 'name',
+    label: '任务名称',
+    search: {
+      el: 'input'
+    },
+    width: 150
+  },
+  {
+    prop: 'taskType',
+    label: '任务类型',
+    search: {
+      el: 'select'
+    },
+    width: 100,
+    tag: true,
+    enum: taskTypeEnums
+  },
+  {
+    prop: 'status',
+    label: '任务状态',
+    search: {
+      el: 'select'
+    },
+    width: 100,
+    tag: true,
+    enum: statusEnums
+  },
+  {
+    prop: 'startTime',
+    label: '开始时间',
+    width: 180
+  },
+  {
+    prop: 'endTime',
+    label: '结束时间',
+    width: 180
+  },
+  {
+    prop: 'costSecond',
+    label: '耗时',
+    search: {
+      el: 'input'
+    },
+    width: 80
+  },
+  {
+    prop: 'log',
+    label: '日志',
+    width: 120
+  },
+
+  {
+    prop: 'algorithmPath',
+    label: '日志路径',
+    width: 120
+  },
+  {
+    prop: 'hyperparameterConfiguration',
+    label: '超参配置',
+    width: 120
+  },
+  {
+    prop: 'remarks',
+    label: '备注',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'operation',
+    label: '操作',
+    width: 230,
+    fixed: 'right'
+  }
+])
+// 表单配置项
+let itemsOptions: ProForm.ItemsOptions[] = [
+  {
+    label: '任务名称',
+    prop: 'name',
+    rules: [{ required: true, message: '任务名称不能为空', trigger: 'blur' }],
+    compOptions: {
+      placeholder: '请输入任务名称'
+    }
+  },
+  {
+    label: '任务类型',
+    prop: 'taskType',
+    rules: [{ required: true, message: '任务类型不能为空', trigger: 'change' }],
+    compOptions: {
+      elTagName: 'select', // 指定使用 el-select 组件
+      placeholder: '请选择任务类型',
+      enum: taskType,
+      onChange: (value: string) => {
+        model.value['taskType'] = value
+        hyperparameterConfiguration.forEach(obj => {
+          if (value in obj) {
+            // console.log(obj[value])
+            addParams(obj[value], value)
+            openDialog(1, '任务新增')
+          }
+        })
+      }
+    }
+  },
+  {
+    label: '图片集压缩包',
+    prop: 'inputOssId',
+    rules: [{ required: true, message: '数据压缩包不能为空', trigger: 'change' }],
+    compOptions: {
+      elTagName: 'file-upload',
+      fileSize: 4096,
+      fileType: ['zip'],
+      placeholder: '请上传图片集压缩包'
+    }
+  },
+  {
+    label: '备注',
+    prop: 'remarks',
+    rules: [
+      {
+        required: false,
+        trigger: 'blur'
+      }
+    ],
+    compOptions: {
+      placeholder: '请输入备注'
+    }
+  }
+]
+const model = ref({})
+const setItemsOptions = () => {
+  if (itemsOptions.length > 4) {
+    itemsOptions.splice(4) // 如果里面有新增参数,删除,重新添加
+  }
+}
+
+const addParams = (params, task_Type) => {
+  setItemsOptions()
+  if (params == 'null') {
+    return
+  }
+  let validJsonString = params.replace(/'/g, '"')
+  if (task_Type === '多目标跟踪') {
+    itemsOptions.push({
+      label: '模型',
+      prop: 'yolo_model',
+      rules: [{ trigger: 'blur' }],
+      compOptions: {
+        elTagName: 'select',
+        enum: modelType,
+        placeholder: '默认值为' + 'yolov8__best.pt'
+      }
+    })
+    itemsOptions.push({
+      label: '跟踪方法',
+      prop: 'tracking_method',
+      rules: [{ trigger: 'blur' }],
+
+      compOptions: {
+        elTagName: 'select',
+        enum: trackingMethod,
+        placeholder: '默认值为' + 'bytetrack'
+      }
+    })
+  }
+  model.value['tracking_method'] = 'bytetrack'
+  model.value['yolo_model'] = 'yolov8__best.pt'
+  try {
+    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]
+        }
+      })
+    })
+  } catch (error) {
+    console.error('解析 JSON 字符串时出错:', error)
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.image-dialog {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  .el-image {
+    margin-right: 20px;
+    margin-bottom: 20px;
+  }
+}
+.image-dialog-btn {
+  display: flex;
+  justify-content: center;
+  margin-top: 20px;
+}
+</style>