feat(facemask): 黑丝网眼加粗并给 Seedance 提示词追加忽略网格说明
This commit is contained in:
@@ -278,13 +278,17 @@ func applyFaceNotice(b []byte) ([]byte, error) {
|
|||||||
// drawStocking 在给定区域上盖一层黑丝网眼:细密的深色网格线,遮住五官细节但保留轮廓。
|
// drawStocking 在给定区域上盖一层黑丝网眼:细密的深色网格线,遮住五官细节但保留轮廓。
|
||||||
func drawStocking(dst *image.RGBA, r image.Rectangle) {
|
func drawStocking(dst *image.RGBA, r image.Rectangle) {
|
||||||
r = r.Intersect(dst.Bounds())
|
r = r.Intersect(dst.Bounds())
|
||||||
step := r.Dx() / 18
|
step := r.Dx() / 24
|
||||||
if step < 4 {
|
if step < 3 {
|
||||||
step = 4
|
step = 3
|
||||||
|
}
|
||||||
|
line := step / 2
|
||||||
|
if line < 1 {
|
||||||
|
line = 1
|
||||||
}
|
}
|
||||||
for y := r.Min.Y; y < r.Max.Y; y++ {
|
for y := r.Min.Y; y < r.Max.Y; y++ {
|
||||||
for x := r.Min.X; x < r.Max.X; x++ {
|
for x := r.Min.X; x < r.Max.X; x++ {
|
||||||
if (x-r.Min.X)%step != 0 && (y-r.Min.Y)%step != 0 {
|
if (x-r.Min.X)%step >= line && (y-r.Min.Y)%step >= line {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c := dst.RGBAAt(x, y)
|
c := dst.RGBAAt(x, y)
|
||||||
@@ -295,3 +299,7 @@ func drawStocking(dst *image.RGBA, r image.Rectangle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// faceMaskPromptNote 附加到 Seedance 提示词后:告知模型参考图脸部的网格线只是打码,
|
||||||
|
// 需要忽略网格本身并完整还原面部细节。
|
||||||
|
const faceMaskPromptNote = "参考图人物脸部覆盖的细密网格线仅为隐私打码,不是人物本身的特征:生成时请完全忽略这些网格线,不要在画面中出现任何网格、方格、纹理或遮挡;请依据参考图的五官轮廓完整还原人物真实面孔,保留妆容、眉眼、发型、发饰、耳饰、头冠等一切面部与头部装饰细节,人物面部必须清晰完整、前后镜头保持一致。"
|
||||||
|
|||||||
@@ -1803,7 +1803,9 @@ func (s *V1Service) generateAdobeVideo(ctx context.Context, eventID string, mode
|
|||||||
}
|
}
|
||||||
// Seedance 参考图先做人脸打码再上传;检不出人脸就沿用原图,
|
// Seedance 参考图先做人脸打码再上传;检不出人脸就沿用原图,
|
||||||
// 检测器不可用则直接报错,避免把未打码的人脸传给上游。
|
// 检测器不可用则直接报错,避免把未打码的人脸传给上游。
|
||||||
|
prompt := in.Prompt
|
||||||
if isSeedanceModel(modelItem.ID) {
|
if isSeedanceModel(modelItem.ID) {
|
||||||
|
faceMasked := false
|
||||||
for i, r := range imgRefs {
|
for i, r := range imgRefs {
|
||||||
marked, mErr := applyFaceNotice(r)
|
marked, mErr := applyFaceNotice(r)
|
||||||
if errors.Is(mErr, ErrNoFaceDetected) {
|
if errors.Is(mErr, ErrNoFaceDetected) {
|
||||||
@@ -1813,6 +1815,10 @@ func (s *V1Service) generateAdobeVideo(ctx context.Context, eventID string, mode
|
|||||||
return nil, "", fmt.Errorf("face mask: %w", mErr)
|
return nil, "", fmt.Errorf("face mask: %w", mErr)
|
||||||
}
|
}
|
||||||
imgRefs[i] = marked
|
imgRefs[i] = marked
|
||||||
|
faceMasked = true
|
||||||
|
}
|
||||||
|
if faceMasked {
|
||||||
|
prompt = strings.TrimSpace(prompt + "\n\n" + faceMaskPromptNote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1859,7 +1865,7 @@ func (s *V1Service) generateAdobeVideo(ctx context.Context, eventID string, mode
|
|||||||
}
|
}
|
||||||
audioBlobIDs = append(audioBlobIDs, id)
|
audioBlobIDs = append(audioBlobIDs, id)
|
||||||
}
|
}
|
||||||
bytes, meta, genErr := s.adobe.GenerateVideo(ctx, token.Value, engine, in.Prompt, aspectRatio, durationSeconds, resolution, referenceMode, upstreamModel, blobIDs, videoBlobIDs, audioBlobIDs, downloadResult)
|
bytes, meta, genErr := s.adobe.GenerateVideo(ctx, token.Value, engine, prompt, aspectRatio, durationSeconds, resolution, referenceMode, upstreamModel, blobIDs, videoBlobIDs, audioBlobIDs, downloadResult)
|
||||||
if genErr == nil {
|
if genErr == nil {
|
||||||
videoURL = strings.TrimSpace(stringValue(meta["video_url"]))
|
videoURL = strings.TrimSpace(stringValue(meta["video_url"]))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user