Commit d42bb840 authored by Administrator's avatar Administrator

样本处理改成上传多个视频,未完成

parent 54d92562
<script setup lang="ts">
import utils from '@/utils/utils'
import { onUpdated, onMounted, reactive, ref } from 'vue'
import { detectDeviceType } from './compositions/common'
import { myFileUpload } from './compositions/fileUpload'
import { useCropBox } from './compositions/useCropBox'
import { onProcessing } from './compositions/process'
const title = ref('元芒数字')
const form = reactive({
source: '',
task_id: '',
file_path: '',
file_path_domain: '',
file_name: '',
file_size: 0,
crop_range: {},
box_range: {},
is_set: false,
sample_path: '',
})
const steps_active = ref(0)
const FileSizeLimitM = 500
const file_loading = ref(false)
const my_file_upload = myFileUpload(FileSizeLimitM, form, file_loading)
const upload = my_file_upload.upload
const actionUrl = my_file_upload.actionUrl
// 裁剪
const videoPlayer = ref<HTMLVideoElement | null>(null)
const canvas = ref<HTMLCanvasElement | null>(null)
const ctx = ref<CanvasRenderingContext2D | null>(null)
const cropBox = useCropBox(form, canvas, ctx)
const result_loading = ref(false)
const processing = onProcessing(form, steps_active, result_loading)
onMounted(async () => {
// 检测设备类型
form.source = detectDeviceType();
// 设置页面标题
document.title = title.value;
// 生成task_id
form.task_id = utils.genDateTimeStr();
console.log('页面加载,task_id =', form.task_id);
})
onUpdated(() => {
if (steps_active.value === 0 && form.is_set && form.sample_path !== '') {
console.log('切换到步骤 0 时执行脚本');
form.task_id = utils.genDateTimeStr();
console.log('返回首页,task_id =', form.task_id);
// 显示裁剪框
setTimeout(() => {
console.log('waiting for video size...')
cropBox.generate_canvas();
}, 1000)
}
})
</script>
<template>
<main class="home-container">
<!-- 标题 -->
<div class="title"><el-text>生成训练样本</el-text></div>
<div class="subtitle"><el-text>上传视频生成训练样本</el-text></div>
<!-- 步骤条 -->
<div>
<el-steps :active="steps_active" align-center finish-status="success">
<el-step title="上传视频并设置范围"/>
<!-- <el-step title="选取背景图"/> -->
<el-step title="生成样本"/>
</el-steps>
</div>
<div class="content-container">
<!-- 文件页 -->
<div class="upload-section" v-if="steps_active === 0">
<div><el-text class="section-title">上传文件、设置范围</el-text></div>
<div><el-text class="section-desc">上传视频后,可以播放、暂停,然后点击设置,先画裁剪框,再画扫描框</el-text></div>
<div class="upload-div" v-loading="file_loading" v-if="!form.file_path">
<el-upload
ref="upload"
:show-file-list="false"
:limit="1"
drag
accept=".mp4"
:action="actionUrl"
:on-success="my_file_upload.handleUploadSuccess"
:on-exceed="my_file_upload.handleUploadExceed"
:on-error="my_file_upload.handleUploadError"
:on-remove="my_file_upload.handleRemoveFile"
:before-upload="my_file_upload.handleBeforeUpload"
:on-progress="my_file_upload.handleUploadProgress"
list-type="picture"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">拖拽文件到这里 或 <em>浏览文件</em></div>
<div class="el-upload__tip" slot="tip">文件格式mp4, 大小限制{{FileSizeLimitM}}M</div>
</el-upload>
</div>
<div class="uploaded-div" v-if="form.file_path" >
<div class="uploaded-file-info">
<video id="video-player" :src="form.file_path_domain" controls></video>
<canvas
id="crop-canvas"
style="background-color: rgba(255, 0, 0, 0.1); position: absolute; left: 0; z-index: -1;"
@mousedown="cropBox.startDrawing"
@mousemove="cropBox.draw"
@mouseup="cropBox.endDrawing"
>
</canvas>
</div>
<el-button color="#181818" @click="cropBox.generate_canvas">设置</el-button>
<el-button color="#181818" @click="cropBox.reset">重置</el-button>
<p>裁剪框坐标:{{ form.crop_range }}</p>
<p>扫描框坐标:{{ form.box_range }}</p>
<!-- <p>{{ form.task_id }}</p> -->
</div>
<div class="button">
<el-button class="next" color="#181818" size="large" @click="processing.onProcess" :disabled="!form.is_set">Next</el-button>
</div>
</div>
<!-- 结果页 -->
<div class="report-section" v-if="steps_active === 1">
<div><el-text class="section-title">结果</el-text></div>
<div><el-text class="section-desc">下载样本</el-text></div>
<!-- 预览 -->
<div v-loading="result_loading">
<el-text class="progress-info-sub">
{{ form.sample_path}}
</el-text>
</div>
<!-- 按钮 -->
<div class="button">
<el-button class="back" size="large" @click="processing.back_to_first">Back</el-button>
<el-button class="next" color="#181818" size="large"
@click="processing.downloadFile(form.sample_path)"
:disabled="!(form.sample_path && form.sample_path.length > 0)"
>Download</el-button>
</div>
</div>
</div>
</main>
</template>
<style lang="scss" scoped src="./index.css"></style>
......@@ -6,10 +6,11 @@ import {
type UploadProps,
type UploadRawFile,
type UploadFile,
type UploadFiles,
} from 'element-plus'
export const myFileUpload = (FileSizeLimitM: number, form: any, loading: any) => {
export const myFileUpload = (FileSizeLimitM: number, form: any, loading: any, fileList: any) => {
// ############ 处理上传文件 begin #############
const upload = ref<UploadInstance>()
const actionUrl = ref(
......@@ -91,15 +92,90 @@ import {
// ############ 处理上传文件 end #############
// ############ 处理上传多个文件 begin #############
const handleChange: UploadProps['onChange'] = (file: UploadFile, files: UploadFiles) => {
// console.log('handleChange file =', file.name)
// console.log(files)
}
const handleRemoveFileMuti = (file: UploadFile, files: UploadFiles) => {
console.log('移除文件: ', file.name)
console.log('移除文件后剩下: ', files)
form.files.forEach((item: { name: string; size: number; path: string; url: string; box_range: {} }) => {
if (item.name === file.name) {
form.files.pop(item)
console.log('form.files移除:', file.name)
console.log('form.files还剩下', form.files)
}
})
if (files.length == 0) {
console.log('form.files全部移除, form.files=', form.files)
}
}
const handleUploadExceedMuti: UploadProps['onExceed'] = (files) => {
console.log('超出限制的文件:', files)
ElMessage({
message: '超出限制的文件数',
type: 'error'
})
}
const handleBeforeUploadMuti = (file: any) => {
console.log('上传文件:', file.name)
const isLimit = file.size / 1024 / 1024 <= FileSizeLimitM
if (!isLimit) {
ElMessage.error('上传文件大小不能超过 '+FileSizeLimitM+'MB!')
return false
}
}
const handleUploadMuti = () => {
// 提交上传
upload.value!.submit()
}
const handleUploadSuccessMuti = (val: Wm.UploadResult, file: UploadFile, files: UploadFiles) => {
// console.log(val)
if (val.code == 0) {
form.files.push({
name: file.name,
size: file.size,
path: val.data[0].path,
url: val.data[0].url,
box_range: {},
})
ElMessage({
message: file.name + ' 上传成功',
type: 'success'
})
console.log('上传成功(', form.files.length, '), form.files=', form.files)
} else {
ElMessage({
message: file.name + ' 上传失败:' + val.message,
type: 'error'
})
}
}
// ############ 处理上传多个文件 end #############
return {
loading,
upload,
actionUrl,
handleChange,
handleBeforeUpload,
handleUploadSuccess,
handleUploadExceed,
handleUploadError,
handleUploadProgress,
handleRemoveFile,
handleRemoveFileMuti,
handleUploadExceedMuti,
handleBeforeUploadMuti,
handleUploadMuti,
handleUploadSuccessMuti,
}
}
......@@ -8,8 +8,30 @@ import {
import type { Action } from 'element-plus'
export const onProcessing = (form: any, steps_active: any, process_loading: any, result_loading: any) => {
const fileToClassify = async () => {
export const onProcessing = (form: any, steps_active: any, process_loading: any, result_loading: any, fileList: any) => {
const toSet = () => {
// console.log(form.files)
// console.log(fileList.value)
if (!form.files || form.files.length == 0) {
ElMessage({
message: '请先上传文件',
type: 'warning'
})
return
}
if (form.files.length !== fileList.value.length) {
ElMessage({
message: '请点击上传,将所有文件上传',
type: 'warning'
})
return
}
// 进入分类页
from_0_to_1();
}
const toClassify = async () => {
if (!form.file_path || form.file_path.length == 0) {
ElMessage({
message: '请先上传文件',
......@@ -17,8 +39,8 @@ export const onProcessing = (form: any, steps_active: any, process_loading: any,
})
return
}
// 进入第二
from_first_to_second();
// 进入分类
from_1_to_2();
// 清除结果
form.sample_result = {
......@@ -103,8 +125,8 @@ export const onProcessing = (form: any, steps_active: any, process_loading: any,
if (!isAllDataClassified()) {
return
}
// 进入第3
from_second_to_third();
// 进入下载
from_2_to_3();
// 清除结果
form.sample_path = '';
......@@ -153,25 +175,34 @@ export const onProcessing = (form: any, steps_active: any, process_loading: any,
const from_first_to_second = () => {
steps_active.value = 1
const from_0_to_1 = () => {
steps_active.value = 1;
}
const back_to_first = () => {
const back_to_0 = () => {
steps_active.value = 0;
form.task_id = utils.genDateTimeStr();
console.log('返回首页,task_id =', form.task_id);
}
const from_second_to_third = () => {
const from_1_to_2 = () => {
steps_active.value = 2;
}
const back_to_second = () => {
const back_to_1 = () => {
steps_active.value = 1;
}
const from_2_to_3 = () => {
steps_active.value = 3;
}
const back_to_2 = () => {
steps_active.value = 2;
form.classes_select2 = JSON.parse(JSON.stringify(form.classes_select));
}
function downloadFile(url: string, filename?: string) {
const a = document.createElement('a')
a.href = url
......@@ -210,11 +241,14 @@ export const onProcessing = (form: any, steps_active: any, process_loading: any,
return {
from_first_to_second,
back_to_first,
from_second_to_third,
back_to_second,
fileToClassify,
from_0_to_1,
back_to_0,
from_1_to_2,
back_to_1,
from_2_to_3,
back_to_2,
toSet,
toClassify,
classifyToDownload,
downloadFile,
process_loading,
......
......@@ -11,16 +11,16 @@ export function useCropBox(form: any, canvas: any, ctx: any) {
const endY = ref(0)
const boxes = ref<Array<{ start: number[], end: number[] }>>([])
function generate_canvas() {
const el = document.getElementById('crop-canvas') as HTMLCanvasElement
function generate_canvas(idx: number) {
const el = document.getElementById('crop-canvas-'+idx) as HTMLCanvasElement
if (el) {
canvas.value = el
ctx.value = canvas.value.getContext('2d')
// 设置 canvas 尺寸与视频一致
const video = document.getElementById('video-player') as HTMLVideoElement
const video = document.getElementById('video-player-'+idx) as HTMLVideoElement
if (video) {
if (video.videoWidth > 0 && video.videoHeight > 0) {
console.log('video size =', video.videoWidth, video.videoHeight)
console.log('video size =', video.videoWidth, video.videoHeight, '当前:', idx)
canvas.value.width = video.videoWidth
canvas.value.height = video.videoHeight
canvas.value.style.zIndex = '2'
......@@ -56,7 +56,7 @@ export function useCropBox(form: any, canvas: any, ctx: any) {
drawRect(startX.value, startY.value, endX.value - startX.value, endY.value - startY.value)
}
function endDrawing() {
function endDrawing(idx: number) {
if (!isDrawing.value) return
isDrawing.value = false
boxes.value.push({
......@@ -65,29 +65,10 @@ export function useCropBox(form: any, canvas: any, ctx: any) {
})
clearCanvas()
drawBoxes()
console.log('结束绘制,终点:', endX.value, endY.value)
console.log('框', boxes.value.length, '个: ', boxes.value)
// if (boxes.value.length === 1) {
// form.crop_range = boxes.value[0]
// }
// if (boxes.value.length === 2) {
// if (boxes.value[1].start[0] < boxes.value[0].start[0]
// || boxes.value[1].start[1] < boxes.value[0].start[1]
// || boxes.value[1].end[0] > boxes.value[0].end[0]
// || boxes.value[1].end[1] > boxes.value[0].end[1]) {
// ElMessage({
// message: '扫描框必须在裁剪框内',
// type: 'warning'
// })
// reset()
// } else {
// form.box_range = boxes.value[1]
// form.is_set = true
// }
// }
console.log('结束绘制,终点:', endX.value, endY.value, '当前:', idx)
console.log('框', boxes.value.length, '个: ', boxes.value, '当前:', idx)
if (boxes.value.length === 1) {
form.box_range = boxes.value[0]
form.is_set = true
form.files[idx].box_range = boxes.value[0]
}
}
......@@ -109,7 +90,7 @@ export function useCropBox(form: any, canvas: any, ctx: any) {
ctx.value!.clearRect(0, 0, c.width, c.height)
}
function reset() {
function reset(idx: number) {
if (boxes.value.length === 0) {
return
}
......@@ -117,10 +98,9 @@ export function useCropBox(form: any, canvas: any, ctx: any) {
boxes.value.pop()
}
clearCanvas()
form.crop_range = {}
form.box_range = {}
form.files[idx].box_range = {}
form.is_set = false
console.log('重置绘制数据')
console.log('重置绘制数据', idx)
}
return {
......
......@@ -64,6 +64,19 @@
/* border: 2px dashed #dddfe5; */
border-radius: 4px;
}
/* 按钮 */
.button {
display: flex;
flex-direction: column;
align-items: flex-start;
.next {
align-self: flex-end;
}
}
}
/* 设置页 */
.set-section {
/* 已上传文件 */
.uploaded-div {
margin: 20px 0;
......@@ -78,12 +91,11 @@
}
/* 按钮 */
.button {
margin: 20px 0;
display: flex;
flex-direction: column;
align-items: flex-start;
.next {
align-self: flex-end;
}
flex-direction: row; /* 修改为行排列 */
justify-content: space-between; /* 使按钮两端对齐 */
align-items: center; /* 垂直居中对齐 */
}
}
......
......@@ -5,6 +5,7 @@ import { detectDeviceType } from './compositions/common'
import { myFileUpload } from './compositions/fileUpload'
import { useCropBox } from './compositions/useCropBox'
import { onProcessing } from './compositions/process'
import type { UploadUserFile } from 'element-plus'
import {
Check,
Delete,
......@@ -14,6 +15,13 @@ import {
Star,
} from '@element-plus/icons-vue'
interface MyUploadFile {
name: string;
size: number;
path: string;
url: string;
box_range: {};
}
const title = ref('元芒数字')
const form = reactive({
......@@ -23,6 +31,7 @@ const form = reactive({
file_path_domain: '',
file_name: '',
file_size: 0,
files: [] as MyUploadFile[], // 上传的文件列表
crop_range: {},
box_range: {},
is_set: false,
......@@ -42,9 +51,11 @@ const form = reactive({
image_width: '100',
})
const steps_active = ref(0)
const FileSizeLimitM = 500
const FileSizeLimitM = 100
const FilesLimit = 5
const fileList = ref<UploadUserFile[]>([])
const file_loading = ref(false)
const my_file_upload = myFileUpload(FileSizeLimitM, form, file_loading)
const my_file_upload = myFileUpload(FileSizeLimitM, form, file_loading, fileList)
const upload = my_file_upload.upload
const actionUrl = my_file_upload.actionUrl
// 裁剪
......@@ -56,7 +67,7 @@ const cropBox = useCropBox(form, canvas, ctx)
const classes = [{value:'0',label:'0'},{value:'1',label:'1'},{value:'2',label:'2'},{value:'3',label:'3'}]
const process_loading = ref(false)
const result_loading = ref(false)
const processing = onProcessing(form, steps_active, process_loading, result_loading)
const processing = onProcessing(form, steps_active, process_loading, result_loading, fileList)
// 目标帧率选项
const fps_options = [
{value: '10', label: '10'},
......@@ -82,9 +93,9 @@ onMounted(async () => {
})
const onVideoLoaded = () => {
if (steps_active.value === 0 && form.is_set && form.sample_result.root_path !== '') {
console.log('切换到步骤 0 视频加载完成');
cropBox.generate_canvas();
if (steps_active.value === 1 && form.is_set && form.sample_result.root_path !== '') {
console.log('切换到步骤 1 视频加载完成');
// cropBox.generate_canvas();
}
};
......@@ -99,7 +110,8 @@ const onVideoLoaded = () => {
<!-- 步骤条 -->
<div>
<el-steps :active="steps_active" align-center finish-status="success">
<el-step title="上传视频并设置范围"/>
<el-step title="上传视频"/>
<el-step title="设置范围"/>
<el-step title="样本分类"/>
<el-step title="下载样本"/>
</el-steps>
......@@ -108,69 +120,76 @@ const onVideoLoaded = () => {
<div class="content-container">
<!-- 文件页 -->
<div class="upload-section" v-if="steps_active === 0">
<div><el-text class="section-title">上传文件、设置范围</el-text></div>
<div><el-text class="section-desc">上传视频后,可以播放、暂停,然后点击设置,先画裁剪框,再画扫描框</el-text></div>
<div><el-text class="section-title">上传文件(可以上传多个)</el-text></div>
<div><el-text class="section-desc">选好视频后,点击上传</el-text></div>
<div class="upload-div" v-loading="file_loading" v-if="!form.file_path">
<el-upload
v-model:file-list="fileList"
ref="upload"
:show-file-list="false"
:limit="1"
drag
:show-file-list="true"
:limit="FilesLimit"
accept=".mp4"
:action="actionUrl"
:on-success="my_file_upload.handleUploadSuccess"
:on-exceed="my_file_upload.handleUploadExceed"
:auto-upload="false"
:multiple="true"
:on-change="my_file_upload.handleChange"
:on-success="my_file_upload.handleUploadSuccessMuti"
:on-exceed="my_file_upload.handleUploadExceedMuti"
:on-error="my_file_upload.handleUploadError"
:on-remove="my_file_upload.handleRemoveFile"
:before-upload="my_file_upload.handleBeforeUpload"
:on-progress="my_file_upload.handleUploadProgress"
list-type="picture"
:on-remove="my_file_upload.handleRemoveFileMuti"
:before-upload="my_file_upload.handleBeforeUploadMuti"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">拖拽文件到这里 或 <em>浏览文件</em></div>
<div class="el-upload__tip" slot="tip">文件格式mp4, 大小限制{{FileSizeLimitM}}M</div>
<template #trigger><el-button type="primary">选择文件</el-button></template>
<el-button type="success" @click="my_file_upload.handleUploadMuti" style="margin-left: 10px;">上传</el-button>
<template #tip><div class="el-upload__tip">文件格式mp4,单个限制{{FileSizeLimitM}}M,可上传{{FilesLimit}}</div></template>
</el-upload>
</div>
<div class="uploaded-div" v-if="form.file_path" >
<div class="button">
<el-button class="next" color="#181818" size="large" @click="processing.toSet">Next</el-button>
</div>
</div>
<!-- 设置页 -->
<div class="set-section" v-if="steps_active === 1">
<div><el-text class="section-desc">逐个点击设置,画扫描框</el-text></div>
<el-text style="color: #181818; font-weight: bold;">目标帧率:</el-text>
<el-select v-model="form.target_fps" placeholder="请选择" style="width: 80px" size="small">
<el-option
v-for="item in fps_options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<div class="uploaded-div" v-for="(file, index) in form.files" :key="index">
<div class="uploaded-file-info">
<video id="video-player" :src="form.file_path_domain" controls @loadeddata="onVideoLoaded"></video>
<video :id="'video-player-'+index" :src="file.url" controls @loadeddata="onVideoLoaded"></video>
<canvas
id="crop-canvas"
:id="'crop-canvas-'+index"
style="background-color: rgba(255, 0, 0, 0.1); position: absolute; left: 0; z-index: -1;"
@mousedown="cropBox.startDrawing"
@mousemove="cropBox.draw"
@mouseup="cropBox.endDrawing"
@mouseup="cropBox.endDrawing(index)"
>
</canvas>
</div>
<el-button color="#181818" @click="cropBox.generate_canvas">设置</el-button>
<el-button color="#181818" @click="cropBox.reset">重置</el-button>
<el-text style="color: #181818; margin-left: 10px; font-weight: bold;">目标帧率:</el-text>
<el-select v-model="form.target_fps" placeholder="请选择" style="width: 80px">
<el-option
v-for="item in fps_options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<!-- <p>裁剪框坐标:{{ form.crop_range }}</p> -->
<p>扫描框坐标:{{ form.box_range }}</p>
<!-- <p>{{ form.task_id }}</p> -->
<el-button color="#181818" @click="cropBox.generate_canvas(index)">设置</el-button>
<el-button color="#181818" @click="cropBox.reset(index)">重置</el-button>
<p>扫描框坐标:{{ file.box_range }}</p>
</div>
<div class="button">
<el-button class="back" size="large" @click="processing.back_to_0">Back</el-button>
<el-button class="next" color="#181818" size="large"
@click="processing.fileToClassify"
@click="processing.toClassify"
:disabled="!form.is_set">Next</el-button>
</div>
</div>
<!-- 处理页 -->
<div class="progress-section" v-if="steps_active === 1">
<div class="progress-section" v-if="steps_active === 2">
<div><el-text class="section-title">样本分类</el-text></div>
<div>
<el-text class="section-desc">当前fps={{ form.target_fps }},图片显示大小:</el-text>
......@@ -210,7 +229,7 @@ const onVideoLoaded = () => {
<!-- <p>{{ form.classes_select }}</p> -->
<!-- 按钮 -->
<div class="button">
<el-button class="back" size="large" @click="processing.back_to_first">Back</el-button>
<el-button class="back" size="large" @click="processing.back_to_1">Back</el-button>
<el-button class="next" color="#181818" size="large"
@click="processing.classifyToDownload"
>Next</el-button>
......@@ -220,7 +239,7 @@ const onVideoLoaded = () => {
<!-- 结果页 -->
<div class="report-section" v-if="steps_active === 2">
<div class="report-section" v-if="steps_active === 3">
<div><el-text class="section-title">结果</el-text></div>
<div><el-text class="section-desc">下载样本</el-text></div>
<!-- 内容 -->
......@@ -231,7 +250,7 @@ const onVideoLoaded = () => {
</div>
<!-- 按钮 -->
<div class="button">
<el-button class="back" size="large" @click="processing.back_to_second">Back</el-button>
<el-button class="back" size="large" @click="processing.back_to_2">Back</el-button>
<el-button class="next" color="#181818" size="large"
@click="processing.downloadFile(form.sample_path)"
:disabled="!(form.sample_path && form.sample_path.length > 0)"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment