This commit is contained in:
2026-09-04 14:25:23 +08:00
parent 8f5810f0d4
commit b3c43ef0fe
5 changed files with 21 additions and 8 deletions
+14 -3
View File
@@ -1027,7 +1027,8 @@ func (s *datasetService) buildPromptFromTemplate(ctx context.Context, dataset *e
return pool
}
scene, action, occlusion := "", "", ""
if bundle := pickSceneEntry(parseScenePool(dataset.GenScenes)); bundle != nil {
bundle := pickSceneEntry(parseScenePool(dataset.GenScenes))
if bundle != nil {
scene = bundle.Scene
if len(bundle.Actions) > 0 {
action = pickCfgList(bundle.Actions)
@@ -1053,6 +1054,12 @@ func (s *datasetService) buildPromptFromTemplate(ctx context.Context, dataset *e
}
// 距离描述从表单固定值生成(25 → "25米外"
distWord := fmt.Sprintf("%d米外", dist)
// 位置锚 {position}:默认地平线锚(地面/水面场景,目标在地平线附近语义成立);
// 捆绑组 position 覆盖(树栖场景目标须锚到枝头/树冠,否则「地平线附近有且仅有斑鸠」仍落在地面)
position := "在" + distWord + "的地平线附近"
if bundle != nil && strings.TrimSpace(bundle.Position) != "" {
position = strings.ReplaceAll(bundle.Position, "{distanceWord}", distWord)
}
// 尺寸提示:按物理公式把表单距离换算成目标在图中的像素高与画面占比
sizeHint, pctVal, sErr := s.buildSizeHint(ctx, dataset, dist, size)
if sErr != nil {
@@ -1079,6 +1086,7 @@ func (s *datasetService) buildPromptFromTemplate(ctx context.Context, dataset *e
"{scene}": scene, "{species}": sexWord + species, "{count}": countWord,
"{action}": action, "{occlusion}": occlusion, "{light}": light,
"{distanceWord}": distWord, "{sizeHint}": sizeHint, "{tone}": tone,
"{position}": position,
} {
out = strings.ReplaceAll(out, k, v)
}
@@ -1162,13 +1170,16 @@ func pickCfgList(pool []string) string {
}
// genSceneEntry 场景池条目:字符串条目解析为仅含 Scene 的本结构(weight=1,动作/遮挡走全局池);
// 捆绑组对象 {scene, weight, actions, occlusions} 的动作/遮挡与场景联动(2026-09-04
// 斑鸠树栖 80%——三池独立随机会拼出「树上场景+地面动作/草丛遮挡」矛盾提示词,见技术设计.md)
// 捆绑组对象 {scene, weight, actions, occlusions, position} 的动作/遮挡与场景联动(2026-09-04
// 斑鸠树栖 80%——三池独立随机会拼出「树上场景+地面动作/草丛遮挡」矛盾提示词,见技术设计.md)
// position 覆盖默认位置锚「在N米外的地平线附近」(树栖目标锚地平线=语义落地,须锚到枝头,
// 内可用 {distanceWord} 占位)
type genSceneEntry struct {
Scene string `json:"scene"`
Weight int `json:"weight"`
Actions []string `json:"actions"`
Occlusions []string `json:"occlusions"`
Position string `json:"position"`
}
// parseScenePool 解析场景池 JSON 数组:条目兼容字符串与捆绑组对象,非法条目跳过
+6 -4
View File
@@ -41,19 +41,21 @@ imageGen:
# 通用提示词模板:{species} 取数据集 gen_species(单值,空回退数据集名);{count} 为数量词
# (固定 "1只",每张仅 1 个目标;原表单数量选择已取消);{scene}/{action}/{light} 池内随机
# scene/action/occlusion 从数据集表 gen_scenes/gen_actions/gen_occlusions 池随机,无 config 兜底;
# gen_scenes 条目兼容捆绑组对象 {scene,weight,actions,occlusions}——选中时动作/遮挡组内联动,
# gen_scenes 条目兼容捆绑组对象 {scene,weight,actions,occlusions,position}——选中时动作/遮挡组内联动,
# position 覆盖默认位置锚(树栖场景须把目标锚到枝头,默认地平线锚只适合地面/水面),
# 见技术设计.md「场景捆绑组+权重」,斑鸠 80% 树上场景即此机制);
# {tone} 取数据集表 gen_tone(默认深色,白化个体配浅色);{sizeHint} 按数据集表 gen_heights 站高与
# 表单距离算出像素高,按画面占比分级尺寸词(像素数与固定物体类比词均不通用,2026-08-31)+ 占比。物种只写名字
# 不写羽毛细节(细节会把镜头拉近,详见技术设计)。引导语「手机原相机主摄不变焦」(2026-08-31 v4 定案):
# 与 13px 像素数学完全自洽(公式即按主摄 vfov 52° 计算);手机拍远处目标天然显小(先验同向);
# 顺带对齐推理端域(App 就是手机拍摄)。演进:广角(前景大主体先验)→ 长焦(物理放大)→ 全删 → 手机主摄不变焦。
# 拍摄角度描述(仰/俯/平)仍不带;位置锚「在{distanceWord}的地平线附近」(曾因"上部1/3"遵循力弱去掉位置描述,改地平线锚点重试);
# 拍摄角度描述(仰/俯/平)仍不带;位置锚 {position}(默认「在{distanceWord}的地平线附近」,地平线=远景场景锚点,
# 适合地面/水面;捆绑组 position 可覆盖为枝头/树冠锚——2026-09-04,树栖目标锚在地平线上语义落地);
# 动作/遮挡在普通模板中位于目标之后(动作=叙事焦点=视觉焦点,降级处理)
promptTemplate: "野外大远景实拍:手机原相机主摄不变焦,{scene},在{distanceWord}的地平线附近有且仅有{count}{species}{sizeHint}{occlusion}{species}在{action},只是很小的{tone}轮廓,隐约可辨,画面绝大部分是{scene},没有任何靠近镜头的动物,{light},写实野外摄影,真实照片质感"
promptTemplate: "野外大远景实拍:手机原相机主摄不变焦,{scene},{position}有且仅有{count}{species}{sizeHint}{occlusion}{species}在{action},只是很小的{tone}轮廓,隐约可辨,画面绝大部分是{scene},没有任何靠近镜头的动物,{light},写实野外摄影,真实照片质感"
# 远景小目标模板(代码按尺寸占比 <2% 切换):目标小到动作/遮挡/性别细节无法呈现,保留细节描写会
# 迫使模型把目标画大(2026-08-31 实测两次失败后定案)——只留「远到看不清任何细节的小点」
promptTemplateTiny: "野外大远景实拍:手机原相机主摄不变焦,{scene},在{distanceWord}的地平线附近有且仅有{count}{species}{sizeHint},远到看不清任何细节,只是隐约可辨的{tone}小点,画面绝大部分是{scene},没有任何靠近镜头的动物,{light},写实野外摄影,真实照片质感"
promptTemplateTiny: "野外大远景实拍:手机原相机主摄不变焦,{scene},{position}有且仅有{count}{species}{sizeHint},远到看不清任何细节,只是隐约可辨的{tone}小点,画面绝大部分是{scene},没有任何靠近镜头的动物,{light},写实野外摄影,真实照片质感"
lights: [清晨柔和阳光, 正午明亮天光, 傍晚金色斜阳, 阴天柔和散射光]
# 目标尺寸提示(sizeHint):按「体高 × focalPx ÷ 表单距离」算出目标在图中的像素高与占比,
# 注入模板 {sizeHint}——把「厘米」换算成「米」再参与投影公式;focalPx = 图高/2 ÷ tan(vfov/2)
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -350,7 +350,7 @@ Android 客户端启动 GET /api/v1/app/update(公开,无需 tokeniOS 不
- **重新生成接口**`POST /datasets/gen-pools``{datasetId}`,物种取数据集名)——编辑对话框「VLM 重新生成参数」按钮入口,VLM 失败报错(不覆盖旧值,旧值保留)
- **读取链(无 config 兜底)**:物种 = 表 `gen_species` 单值 → 空则数据集名本身(**固定,不随机**);场景/动作/遮挡 = 表池随机 → **空则模板组装报错**(提示手填提示词或编辑数据集补参数);`{tone}` = 表 `gen_tone` 单值 → 空默认「深色」;站高 = 表 `gen_heights` 数值(**创建时 VLM 生成,界面不维护,代码/配置不硬编码**)→ ≤0 兜底 35cm(物理默认,非配置回退);标注类别名 = 第一类 `gen_species` + 第二类 `gen_classes`(训练 data.yaml / 模型 labels
- **界面不维护 gen_*2026-08-28 用户定案)**`gen_species/gen_tone/gen_heights/gen_scenes/gen_actions/gen_occlusions/gen_classes` 全部仅由模型生成(创建数据集时 VLM 自动、`gen-pools` 重新生成),管理端编辑表单对 7 列只读展示、无输入框——`POST /datasets/update` 不接收任何 gen 字段;config.yml 无任何 per-dataset 生成参数节点(`speciesByDataset/speciesTone/speciesHeights/sceneByDataset/actionByDataset/occlusionByDataset/scenes/actions/occlusions/localAi.classNames` 已删,代码无引用),保留:`promptTemplate`(通用模板)与 `lights`(光线池)、`assumedVfovDeg`;数据源唯一入口为数据集表(本库存量 15 行已直接改单值 + 真实站高,无启动迁移);**雪地场景补齐(2026-08-31,按冬季习性)**:VLM 生成场景池缺冬季场景(全部为夏秋农田),13 数据集 `gen_scenes` 直接 SQL 追加 2~3 条雪地条目(农田类雉鸡/鸽子、兔子荒坡、白马鸡蓝马鸡高山林缘);**鸭子/鹌鹑不加**——北方冬季南迁(水面封冻、候鸟不越冬),雪地场景与习性矛盾(用户拍板);`genPoolsWithVLM` 提示词已加「按冬季习性配场景:本地活跃物种须含积雪场景,南迁物种不配雪地」防新数据集一刀切;**林缘/树栖场景补齐(2026-08-31,按栖息习性)**:雉类+鸽子共享场景池(10 数据集)缺林地交接处与树上落栖场景,SQL 追加 5 条(2 林缘 + 3 树上)+ 2 条树上动作(枝头停栖/树枝上歇息);**石鸡剔除树上条目(用户定案)**——岩石地栖不上树,3 树上场景 + 2 树上动作从石鸡池移除(保留 2 林缘);**总原则(用户定案):场景与遮挡物必须符合对应动物习性**——场景(雪地/林缘/树上/水面)与遮挡物(草丛/树叶/树枝/芦苇)均按物种习性逐条判断,习性与场景矛盾即剔除;**遮挡规则(适用于所有具备树上场景的动物)**:遮挡不一定是草丛,树栖/林缘物种遮挡池须含树叶/树枝类遮挡(雉类池原已含 2 条;白马鸡/蓝马鸡补 2 条;兔子原已有 1 条林缘树叶);`genPoolsWithVLM` 提示词同步加「树栖物种场景须含林缘与树上落栖」「具备树上/林缘场景的物种遮挡池须含树叶/树枝类遮挡」
- **场景捆绑组+权重(2026-09-04,斑鸠树栖 80% 定案)**`gen_scenes` 条目在字符串外兼容**捆绑组对象** `{scene, weight, actions[], occlusions[]}`——场景池加权随机(weight 缺省 1,普通字符串条目权重 1),选中捆绑组时动作/遮挡仅从组内池随机(与场景联动,杜绝「树上场景+地面动作/草丛遮挡」矛盾组合——三池独立随机按模板拼装矛盾率高);组内缺省或普通字符串条目回落全局 `gen_actions`/`gen_occlusions` 池。落地(2026-09-04 用户定案「每种动物生境权重不同:野鸡主要在地面、斑鸠在树上、鸭子在水里」):
- **场景捆绑组+权重(2026-09-04,斑鸠树栖 80% 定案)**`gen_scenes` 条目在字符串外兼容**捆绑组对象** `{scene, weight, actions[], occlusions[], position}`——场景池加权随机(weight 缺省 1,普通字符串条目权重 1),选中捆绑组时动作/遮挡仅从组内池随机(与场景联动,杜绝「树上场景+地面动作/草丛遮挡」矛盾组合——三池独立随机按模板拼装矛盾率高);组内缺省或普通字符串条目回落全局 `gen_actions`/`gen_occlusions` 池。**position 覆盖位置锚(2026-09-04 用户实测指出树栖目标锚地平线=语义落地)**:模板占位符由写死的「在{distanceWord}的地平线附近」抽成 `{position}`(两模板同改),默认值不变——地面/水面场景目标锚地平线成立;捆绑组 position 覆盖为「在N米外地平线附近…大树的枝头上/枝杈间/粗壮横枝上」(内可用 `{distanceWord}` 占位),树栖目标随场景锚到枝头。落地(2026-09-04 用户定案「每种动物生境权重不同:野鸡主要在地面、斑鸠在树上、鸭子在水里」):
**斑鸠(id 133)** gen_scenes = 4 个树上捆绑组(各 weight 8+ 8 条地面字符串(weight 1)→ 树上恰好 80%gen_actions 收敛为纯地面 4 条(枝头/电线类动作移入捆绑组),树栖遮挡含树叶/树枝类;
**雉类 8 数据集(环颈雉/红腹锦鸡/白腹锦鸡/白鹇/黄腹角雉/灰胸竹鸡/蓝鹇/褐马鸡)**gen_scenes 剔除 4 条树上景观字符串、追加 1 个树上捆绑组 weight 3 → 地面 14/17 ≈ 82%、树上 ≈ 18%(雉类地面觅食为主、树上仅停栖/夜栖),gen_actions 剔除枝头停栖/树枝上歇息(移入捆绑组);
**鸭子(108)** 全水面场景+游动/潜水动作+芦苇遮挡本就自洽,**白马鸡/蓝马鸡** 高山地面/林缘自洽,**兔子/鹌鹑/石鸡** 无树上条目——均不动。