package imagegen import "context" // FaceSwapRequest transfers an identity from Source into Target. // // This is a DIFFERENT OPERATION from Edit, not a better-tuned one. Measured // against the instruction-edit models on 2026-07-31, asking a diffusion model // to put a specific person's face into a photo does not work by any route — // by name, by description, or by supplying the portrait as a reference image. // Face swapping is a dedicated detect/align/blend pipeline; a provider that // cannot do it should not pretend Edit is a substitute. type FaceSwapRequest struct { // Target is the photo to edit — the pose, expression, lighting and // everything outside the face are preserved from it. Target Image // Source is a photo of the face to put in. Only the identity travels; // the source's own pose and expression do not. Source Image // Index selects WHICH face in Target, in the provider's documented // ordering (llamaswap: left to right by box centre, as reported by // ListFaces). nil = the largest face, which is right for a portrait and // wrong for a group — enumerate first when it matters. Index *int // All swaps every detected face and ignores Index. All bool } // FaceSwapOption mutates a FaceSwapRequest before it is sent. type FaceSwapOption func(*FaceSwapRequest) // WithFaceIndex selects which face in the target to replace. func WithFaceIndex(i int) FaceSwapOption { return func(r *FaceSwapRequest) { r.Index = &i } } // WithAllFaces swaps every detected face. func WithAllFaces() FaceSwapOption { return func(r *FaceSwapRequest) { r.All = true } } // Apply returns a copy of the request with all options applied. func (r FaceSwapRequest) Apply(opts ...FaceSwapOption) FaceSwapRequest { for _, opt := range opts { opt(&r) } return r } // DetectedFace is one face located in an image, in PIXEL coordinates. type DetectedFace struct { // Index is the face's position in the provider's stable ordering, and // the value FaceSwapRequest.Index expects. Index int // Box is [x0, y0, x1, y1]. Box [4]int // Score is the detector's confidence, 0-1. Score float64 // Width and Height are the box dimensions, carried so a caller can pick // "the big face" without recomputing them. Width, Height int } // FaceSwapper is the optional face-transfer surface. Separate interface so // existing providers keep compiling; callers type-assert. type FaceSwapper interface { // ListFaces enumerates the faces in an image, in the SAME ordering // FaceSwapRequest.Index uses. Exposed because a caller asked to change // "the man on the right" needs a way to name one face and to check its // own choice against pixel boxes. ListFaces(ctx context.Context, img Image) ([]DetectedFace, error) // FaceSwap transfers Source's identity into Target. FaceSwap(ctx context.Context, req FaceSwapRequest, opts ...FaceSwapOption) (*Result, error) }