This commit is contained in:
2026-09-04 11:22:06 +08:00
parent 19f16aaed3
commit 25846b133a
7 changed files with 44 additions and 19 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>视野管理端</title>
<script type="module" crossorigin src="/admin/assets/index-BYQXb-Nt.js"></script>
<link rel="stylesheet" crossorigin href="/admin/assets/index-CVJMjZta.css">
<script type="module" crossorigin src="/admin/assets/index-Bo35oPaD.js"></script>
<link rel="stylesheet" crossorigin href="/admin/assets/index-CfRFbxBf.css">
</head>
<body>
<div id="app"></div>
+11 -9
View File
@@ -79,9 +79,11 @@ const (
// 按最大 class 0 框的归一化框高 h 划分,边界为占图高百分比,下含上不含。
// <2% 极远档豁免——recall 瓶颈档只缺不多,不进候选清单(配额 0 固定不适用);
// 其余档 Quota 为「目标保留张数」缺省值,preview 请求可覆盖(管理端配额表默认展示即此表)。
// 配额随目标尺寸单调递减(2026-09-02 定案;2026-09-04 按每物种 500 张训练规模放大为方案A):
// 尺寸越大越易检出、冗余切得越狠,小档护稀缺硬样本;可控档合计 470 + <2% 豁免档 ≈ 500。
// 500 规模前(如各档总量 < 配额)配额不生效——超配档才裁剪,旧小数据集不受影响。
// 配额随目标尺寸单调递减(2026-09-02 定案;2026-09-04 按每物种 800 张训练规模放大为方案B
// 覆盖 5m-100m 目标——<2% 档跨 26m→100m+,主动供给 ~300 且集中在 1-2% 子带,
// 0.5% 以下低于 s档可检线不供给;档位↔距离映射与物理上限见技术设计.md):
// 尺寸越大越易检出、冗余切得越狠,小档护稀缺硬样本;可控档合计 520 + <2% 豁免档 ≈ 800。
// 旧小数据集各档总量 < 配额时配额不生效——超配档才裁剪,不受影响。
type CleanBucket struct {
Label string
MinPct float64
@@ -91,11 +93,11 @@ type CleanBucket struct {
var CleanBuckets = []CleanBucket{
{"<2%", 0, 2, 0},
{"2-3%", 2, 3, 100},
{"3-4%", 3, 4, 90},
{"4-6%", 4, 6, 80},
{"6-8%", 6, 8, 60},
{"8-12%", 8, 12, 55},
{"2-3%", 2, 3, 110},
{"3-4%", 3, 4, 100},
{"4-6%", 4, 6, 90},
{"6-8%", 6, 8, 70},
{"8-12%", 8, 12, 60},
{"12-20%", 12, 20, 50},
{">20%", 20, 100, 35},
{">20%", 20, 100, 40},
}
Binary file not shown.
+1 -1
View File
@@ -411,7 +411,7 @@ training:
- **语义(区别于视觉判重)**:不是删「长得像」的图,而是按标注算目标尺寸、按**尺寸档配额**去重——易档(中近距离)堆量、难档(<2% 极远)稀缺是当前数据集冗余的真实形态(2026-09-02 实测:dHash 整图/目标裁剪双重判定,生成图逐张随机 scene/action 几乎无近重复,严格阈值可去近 0 张;而 环颈雉 280 张实拍中 4-8% 档 109 张占 39%——同场地连拍帧为主,删冗余几乎无损)
- **目标尺寸口径**`labels_json` 归一化框高 h 即目标占图高比例(**不需解码图片**);多框取最大 class 0 框定档(候选行展示的主尺寸);**档内保留取舍参考图内最小确认目标**(2026-09-02:多目标图里的小目标样本比主目标稀缺,同档冗余取舍先保它们);像素高 ≈ h×1280(训练尺度,等比近似);**仅 class 1 疑似图与空检出 `[]` 图不入清单**(无确认目标,无从定尺寸,等人工确认后自然进入统计)
- **档位**:按图高占比细档 <2% / 2-3 / 3-4 / 4-6 / 6-8 / 8-12 / 12-20 / >20%——宽档(如 4-8%)跨 2 倍尺度,直接按宽档配额会把半档删光,须细档分别设配额;**<2% 极远档固定豁免**(recall 瓶颈档,只缺不多);各档配额请求可传,缺省用代码默认(consts.go);**配额随目标尺寸单调递减(2026-09-02 用户定案)**:尺寸越大越易检出、单张边际训练价值越低,冗余切得越狠——小档护稀缺硬样本。**默认配额按每物种 500 张训练规模设定(2026-09-04 方案A**2-3%:100 / 3-4%:90 / 4-6%:80 / 6-8%:60 / 8-12%:55 / 12-20%:50 / >20%:35(可控档合计 470 + <2% 豁免档 ≈ 500;500 规模前的旧小数据集各档总量 < 配额时清洗不裁剪,不受影响。配额仅裁剪超配档,未超配档全留——想净留 500 建议准备 550~650 张再按此表跑清洗)。曾用 225 规模默认(60/50/40/30/20/15/10)已随规模上调替换
- **档位**:按图高占比细档 <2% / 2-3 / 3-4 / 4-6 / 6-8 / 8-12 / 12-20 / >20%——宽档(如 4-8%)跨 2 倍尺度,直接按宽档配额会把半档删光,须细档分别设配额;**<2% 极远档固定豁免**(recall 瓶颈档,只缺不多);各档配额请求可传,缺省用代码默认(consts.go);**配额随目标尺寸单调递减(2026-09-02 用户定案)**:尺寸越大越易检出、单张边际训练价值越低,冗余切得越狠——小档护稀缺硬样本。**档位↔距离映射(2026-09-04 定案,主摄 4:3、垂直 FOV≈55°,r ≈ 框高/(距离×0.96))**:同一占比对不同体型动物对应不同距离——2-3% 档 ≈ 雉鸡(框高~0.5m) 17-26m / 野兔(~0.35m) 12-18m / 斑鸠(~0.18m) 6-9m<2% 档 = 雉鸡 >26m / 野兔 >18m / 斑鸠 >9.4m。**物理上限**s档 imgsz 1280 有效图高 960px,占比 0.5% ≈ 5px 低于可靠检出线——100m 处三物种框高均 2~5px 不可检,90% 识别的现实半径(s 档)为雉鸡 ~40-50m / 野兔 ~30m / 斑鸠 ~12-15mn 档 @704 有效高 528px 再减半,远距只有 s 档谈得上)。**默认配额按每物种 800 张训练规模设定(2026-09-04 方案B,覆盖 5m-100m 目标**2-3%:110 / 3-4%:100 / 4-6%:90 / 6-8%:70 / 8-12%:60 / 12-20%:50 / >20%:40(可控档合计 520 + <2% 豁免档主动供给 ~300 且集中在 1-2% 子带(≈雉鸡 26-52m),≈800/物种;0.5% 以下(雉鸡 >52m)低于可检线,不值得花生成预算;500 规模前的旧小数据集各档总量 < 配额时清洗不裁剪,不受影响。配额仅裁剪超配档,未超配档全留——想净留 800 建议准备 900~1000 张再按此表跑清洗)。曾用 225 规模默认(60/50/40/30/20/15/10、500 规模方案A100/90/80/60/55/50/35已随规模上调替换
- **桶内多样性保留**:超配桶内保留谁不随机——**先按图内最小确认目标(class 0)尺寸升序扫描**(小目标样本稀缺,同档图里带更小目标的先占保留名额;同最小尺寸按 id 保持稳定),整图 dHashcommon 层,9x8 灰度 64 位)与已保留图最小汉明距离大于阈值才留,**留够配额即停,其余全进候选清单**(连拍帧天然相邻近哈希,只留首帧;不同场景同尺寸的目标尽量都保);候选行展示主尺寸 + 最小目标占比 + 目标数,供人工判断取舍
- **执行语义(排除而非删除)**:候选清单人工过目后打 `dataset_image.clean_excluded=1`EnsureColumn 迁移)——**只影响训练集打包**(prepare_yolo 跳过),图片/标注不动、列表/标注/预标全流程照常可见;**可恢复**(清除标记即回训练集,付费资产原则:清洗默认不删文件,物理删除仍走人工逐删)
- 接口:`POST /admin/datasets/clean/preview`datasetId + 可选 quotas → 各档分布/配额/超配候选清单 + 已排除列表);`POST /admin/datasets/clean/apply`imageIds + exclude bool 批量置/清标记);**管理端详情页承载:「数据清洗」按钮在已标注 tab 工具条**(配额表可编辑 + 候选清单默认全选 → 执行排除),**已排除的图集中到「已清洗」tab 展示,逐张/批量恢复(apply exclude=false**,图片列表接口返回 cleanExcluded 供前端归类
+28 -5
View File
@@ -117,7 +117,7 @@ async function loadAll() {
// 刷新时恢复进行中的 AI 生成任务(或展示最近一次失败的提示)
const gt = await request.get('/datasets/gen-task', { params: { datasetId: datasetId.value } }).catch(() => null)
if (gt && gt.status === 'running') {
genTask.value = { id: gt.id, status: gt.status, total: gt.total, done: gt.done, error: gt.error }
genTask.value = { id: gt.id, status: gt.status, total: gt.total, done: gt.done, error: gt.error, createdAt: gt.createdAt }
pollGenTask()
} else if (gt && gt.error) {
genTask.value = gt
@@ -153,7 +153,7 @@ function pollGenTask() {
genPoll.value = setInterval(async () => {
const d = await request.get('/datasets/gen-task', { params: { datasetId: datasetId.value } }).catch(() => null)
if (!d) return
genTask.value = { id: d.id, status: d.status, total: d.total, done: d.done, error: d.error }
genTask.value = { id: d.id, status: d.status, total: d.total, done: d.done, error: d.error, createdAt: d.createdAt }
if (d.status !== 'running') {
stopGenPoll()
if (d.error) {
@@ -175,6 +175,29 @@ function stopGenPoll() {
}
}
// 实际速率/预计剩余:按服务端 createdAt 起算(均速=运行时长/已完成张数,刷新页面后依然准);
// done=0 时无速率可估,进度条上先不展示。轮询每 3s 替换 genTaskcomputed 随之重算
function parseGtime(s) {
const t = new Date(String(s || '').replace(' ', 'T'))
return isNaN(t.getTime()) ? null : t
}
function fmtDuration(sec) {
sec = Math.round(sec)
const m = Math.floor(sec / 60)
return m > 0 ? `${m}${sec % 60}` : `${sec}`
}
const genRateSec = computed(() => {
if (!genRunning.value || !genTask.value.done) return 0
const start = parseGtime(genTask.value.createdAt)
if (!start) return 0
const elapsed = (Date.now() - start.getTime()) / 1000
return elapsed > 0 ? elapsed / genTask.value.done : 0
})
const genRateText = computed(() => (genRateSec.value >= 10 ? String(Math.round(genRateSec.value)) : genRateSec.value.toFixed(1)))
const genEtaText = computed(() => (genRateSec.value ? fmtDuration(genRateSec.value * (genTask.value.total - genTask.value.done)) : ''))
function submitGen() {
generating.value = true
request
@@ -187,7 +210,7 @@ function submitGen() {
.then((data) => {
addVisible.value = false
genTask.value = { id: data.taskId, status: 'running', total: data.total, done: 0, error: '' }
ElMessage.success(`已提交生成任务(共 ${data.total},约 40 秒/张),进度见页顶进度条`)
ElMessage.success(`已提交生成任务(共 ${data.total}),实际速率与预计剩余时长见页顶进度条`)
pollGenTask()
})
.catch(() => {})
@@ -1011,7 +1034,7 @@ onBeforeUnmount(() => {
:percentage="genTask.total ? Math.min(100, Math.round((genTask.done / genTask.total) * 100)) : 0"
:stroke-width="10"
/>
<span class="label-progress-tip">AI 生成中 {{ genTask.done }}/{{ genTask.total }} 40 /</span>
<span class="label-progress-tip">AI 生成中 {{ genTask.done }}/{{ genTask.total }} <template v-if="genRateSec">实际约 {{ genRateText }} /预计剩余 {{ genEtaText }}</template></span>
</div>
<div v-if="genTask && genTask.status !== 'running' && genTask.error" class="label-error">
AI 生成未完成{{ genTask.error }}
@@ -1074,7 +1097,7 @@ onBeforeUnmount(() => {
<el-form-item label="图片尺寸">
<span class="gen-size">竖版 704x1248</span>
</el-form-item>
<div class="gen-tip">按本数据集物种{{ dataset?.name || '' }}生成每张固定 1 生成图片为付费资产请勿随意删除批量生成为后台任务 40 /进度见页顶进度条手填提示词时无需写拍摄角度/位置系统自动约束画面中有且只有这一只动物</div>
<div class="gen-tip">按本数据集物种{{ dataset?.name || '' }}生成每张固定 1 生成图片为付费资产请勿随意删除批量生成为后台任务实际速率与预计剩余时长见页顶进度条手填提示词时无需写拍摄角度/位置系统自动约束画面中有且只有这一只动物</div>
</el-form>
<template #footer>