完善生图5.0的功能
This commit is contained in:
1 parent
88a4268d40
commit
678d213f40
31 files changed
+3281
-107
No files matched your search
@@ -0,0 +1,135 @@
|
||||
export type SeedreamCreationMode = "basic" | "interactive" | "layers";
|
||||
|
||||
export type SeedreamPoint = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type SeedreamBox = {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
};
|
||||
|
||||
export type SeedreamAnnotation = {
|
||||
id: string;
|
||||
index: number;
|
||||
kind: "point" | "box" | "stroke";
|
||||
sourceKey?: string;
|
||||
point?: SeedreamPoint;
|
||||
box?: SeedreamBox;
|
||||
points?: SeedreamPoint[];
|
||||
};
|
||||
|
||||
export type SeedreamAnnotationReference = {
|
||||
token: string;
|
||||
label: string;
|
||||
caption: "画布标注";
|
||||
};
|
||||
|
||||
export function seedreamAnnotationToken(index: number, kind: SeedreamAnnotation["kind"] = "box") {
|
||||
return kind === "stroke" ? `@标记${index}` : `@标注${index}`;
|
||||
}
|
||||
|
||||
export function seedreamAnnotationReferences(annotations: SeedreamAnnotation[], query = ""): SeedreamAnnotationReference[] {
|
||||
const normalizedQuery = query.replace(/^@+/, "").trim().toLowerCase();
|
||||
return [...annotations]
|
||||
.sort((a, b) => a.index - b.index)
|
||||
.map((annotation) => {
|
||||
const token = seedreamAnnotationToken(annotation.index, annotation.kind);
|
||||
const label = annotation.kind === "box"
|
||||
? `框选区域 ${annotation.index}`
|
||||
: annotation.kind === "point"
|
||||
? `点选位置 ${annotation.index}`
|
||||
: `蓝色标记 ${annotation.index}`;
|
||||
return { token, label, caption: "画布标注" as const };
|
||||
})
|
||||
.filter((reference) => !normalizedQuery || [reference.token, reference.token.slice(1), reference.label, reference.caption]
|
||||
.some((value) => value.toLowerCase().includes(normalizedQuery)));
|
||||
}
|
||||
|
||||
export function seedreamAnnotationTag(annotation: SeedreamAnnotation, imageIndex?: number) {
|
||||
const prefix = imageIndex && imageIndex > 0 ? `图${imageIndex}` : "";
|
||||
if (annotation.kind === "point" && annotation.point) {
|
||||
return `${prefix}<point>${coordinate(annotation.point.x)} ${coordinate(annotation.point.y)}</point>`;
|
||||
}
|
||||
if (annotation.kind === "box" && annotation.box) {
|
||||
return `${prefix}<bbox>${coordinate(annotation.box.left)} ${coordinate(annotation.box.top)} ${coordinate(annotation.box.right)} ${coordinate(annotation.box.bottom)}</bbox>`;
|
||||
}
|
||||
if (annotation.kind === "stroke" && annotation.points?.length) {
|
||||
return `${prefix || "图片"}中的蓝色标记区域`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function missingSeedreamAnnotationTokens(prompt: string, annotations: SeedreamAnnotation[]) {
|
||||
const available = new Set(annotations.map((annotation) => seedreamAnnotationToken(annotation.index, annotation.kind)));
|
||||
const missing = new Set<string>();
|
||||
for (const match of prompt.matchAll(/@(标注|标记)(\d+)/g)) {
|
||||
const token = `@${match[1]}${match[2]}`;
|
||||
if (!available.has(token)) missing.add(token);
|
||||
}
|
||||
return [...missing].sort((a, b) => annotationTokenIndex(a) - annotationTokenIndex(b));
|
||||
}
|
||||
|
||||
export function buildSeedreamInteractivePrompt(prompt: string, annotations: SeedreamAnnotation[], sourceKeys: string[] = []) {
|
||||
const annotationByIndex = new Map(annotations.map((annotation) => [annotation.index, annotation]));
|
||||
const imageIndexBySource = new Map(sourceKeys.map((key, index) => [key, index + 1]));
|
||||
const referenced = new Set<number>();
|
||||
const transformed = normalizeSeedreamImageTokens(prompt).replace(/@(标注|标记)(\d+)/g, (token, kind: string, rawIndex: string) => {
|
||||
const index = Number(rawIndex);
|
||||
const annotation = annotationByIndex.get(index);
|
||||
if (!annotation || seedreamAnnotationToken(index, annotation.kind) !== token) return token;
|
||||
referenced.add(index);
|
||||
return seedreamAnnotationTag(annotation, imageIndexFor(annotation, imageIndexBySource));
|
||||
}).trim();
|
||||
const unreferenced = annotations
|
||||
.filter((annotation) => !referenced.has(annotation.index))
|
||||
.map((annotation) => `${annotation.kind === "stroke" ? "标记" : "标注"}${annotation.index} ${seedreamAnnotationTag(annotation, imageIndexFor(annotation, imageIndexBySource))}`)
|
||||
.filter((value) => !value.endsWith(" "));
|
||||
if (!unreferenced.length) return transformed;
|
||||
return `${transformed}\n编辑位置:${unreferenced.join(";")}。`.trim();
|
||||
}
|
||||
|
||||
export function buildSeedreamLayerPrompt(prompt: string, annotations: SeedreamAnnotation[]) {
|
||||
const annotationByIndex = new Map(annotations.map((annotation) => [annotation.index, annotation]));
|
||||
const referenced = new Set<number>();
|
||||
const transformed = normalizeSeedreamImageTokens(prompt, true).replace(/@(标注|标记)(\d+)/g, (token, _kind: string, rawIndex: string) => {
|
||||
const index = Number(rawIndex);
|
||||
const annotation = annotationByIndex.get(index);
|
||||
if (!annotation || seedreamAnnotationToken(index, annotation.kind) !== token) return token;
|
||||
referenced.add(index);
|
||||
return seedreamAnnotationTag(annotation);
|
||||
}).trim();
|
||||
const unreferenced = annotations
|
||||
.filter((annotation) => !referenced.has(annotation.index))
|
||||
.map((annotation) => seedreamAnnotationTag(annotation))
|
||||
.filter(Boolean);
|
||||
if (!unreferenced.length) return transformed;
|
||||
const instruction = `需拆分区域:${unreferenced.join(";")}。`;
|
||||
return transformed ? `${transformed}\n${instruction}` : instruction;
|
||||
}
|
||||
|
||||
export function normalizeSeedreamImageTokens(prompt: string, forceFirstImage = false) {
|
||||
return prompt.replace(/@(图片|图)(\d+)/g, (_token, _kind: string, rawIndex: string) => `图${forceFirstImage ? 1 : Number(rawIndex)}`);
|
||||
}
|
||||
|
||||
export function seedreamCreationModeLabel(mode: SeedreamCreationMode) {
|
||||
if (mode === "interactive") return "交互编辑";
|
||||
if (mode === "layers") return "图层拆分";
|
||||
return "基础生图";
|
||||
}
|
||||
|
||||
function coordinate(value: number) {
|
||||
return Math.max(0, Math.min(1000, Math.round(value)));
|
||||
}
|
||||
|
||||
function imageIndexFor(annotation: SeedreamAnnotation, imageIndexBySource: Map<string, number>) {
|
||||
if (annotation.sourceKey) return imageIndexBySource.get(annotation.sourceKey) || 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function annotationTokenIndex(token: string) {
|
||||
return Number(token.match(/\d+$/)?.[0] || 0);
|
||||
}
|
||||
Reference in new issue
Block a user