6995a8dee162ec3dd6b6631fad1a449a156833bd
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6995a8dee1 |
feat(faceswap): expose yaw on enumeration too
ListFaces now carries head yaw, so a caller choosing WHICH face to swap can see the thing that decides whether the swap will read — not only learn it afterwards from the swap report. In the run that prompted this the target's three faces sat at -82, -8 and -11 degrees; only the first was hopeless, and nothing in a bounding box said so. |
||
|
|
ae2615ca68 |
feat(faceswap): carry the measured outcome, not just the image
A face swap always returns an image and always looks like success. Whether the likeness actually transferred is a different question, and until now nothing in the response answered it — so a caller wanting to know went and asked a vision model instead. That is wrong in precisely the cases that matter: shown a jogger in a Georgetown cap holding McDonald's cups, a VLM answers "Bill Clinton" whoever's face is on him. In the run that prompted this it reported failure on six consecutive CORRECT swaps (measured afterwards at 0.79-0.84 cosine), and the caller burned 21 minutes chasing a problem that did not exist. Result.SwappedFaces now carries, per replaced face: pixel size, the target image's dimensions, head yaw, and cosine similarity between the source face and the face actually present in the output. Yaw and FractionOfImage are the two that explain the complaint. The swap in question replaced a 138px face in a 1010px-wide photo — 14% of the width, correct and invisible at a glance — and elsewhere a face turned -82 degrees, where the features carrying identity are edge-on and any swap reads as a generic person. Same code on a 168px face in a 385px picture (44%, yaw 2) is unmistakable. None of that was inferable from a bounding box. Typed on Result rather than stuffed into Raw: a caller has to act on this, and a value reachable only by type-asserting an `any` is one nobody finds in time. doRawHeaders is doRaw with the whole header instead of only Content-Type; doRaw delegates to it, so the other 25 call sites are untouched and there is still one place where the status check and the size cap live. A missing or malformed header yields nil, not an error — an older shim sends no header, and a swap that produced a good image must not fail because the diagnostics beside it were unreadable. Covered for absent/garbage/wrong-type, and the parse is break-checked. |
||
|
|
372bf826aa |
fix(llamaswap): a headerless non-image response was returned as a PNG
Gadfly on #23, blocking, 2/2 agreement — and it is the exact defect this whole line of work has been about: a call that succeeds while handing back the wrong bytes. sniffImageMIME falls back to image/png when detection is inconclusive, and the guard only consulted Content-Type. A response with NO Content-Type therefore skipped the check entirely and was labelled a PNG. The shim answers JSON on a semantic miss (no face found in the source or target), which is precisely the body that would have sailed through as a successful image. The check now validates the BYTES — http.DetectContentType must say image/ — and the reported MIME prefers the server's own label only when that label is itself an image type. Break-checked by restoring the header-only condition, which fails the new test. Also from that review: - index is documented as ignored under all=true, so a negative one is no longer rejected there; it is still rejected when it would actually be sent, and both halves are tested. - initImageFilename (video.go) was imageFilename with the base fixed to "frame" and now delegates to it — two copies of one extension table is how they drift. - DetectedFace carried Width/Height alongside Box, two sources of truth for one fact that can disagree after any transform. Now a Size() method derived from Box. - a dead `apiErr` in the test (declared, then `_ = apiErr`) was an abandoned errors.As check; it is wired up and now asserts callers can classify the error. - swapImg duplicated editInit verbatim; removed. Not taken: adding a FaceSwapProvider/ModelOption surface to match the other optional imagegen capabilities (single-model finding). There are no options to carry yet, and inventing an empty option type to look symmetrical would be API surface with nothing behind it. Worth revisiting when a real knob exists. |
||
|
|
0ff90d80f6 |
feat(imagegen): face swap (identity transfer), a separate operation from Edit
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. qwen-image-edit returns the picture essentially unchanged whether asked by name, by attribute, or by supplying the portrait as a second reference image; flux-kontext replaces the face with a different generic person. Identity transfer is a detect/align/blend pipeline, not a better prompt, so it gets its own interface rather than more Edit options. imagegen.FaceSwapper is optional and type-asserted, like Editor — a provider that cannot do this must not have Edit quietly stand in for it. ListFaces is part of the interface, not a convenience: a caller asked to change "the man on the right" needs a stable way to NAME one face, and pixel boxes let it check its own choice. The llamaswap shim orders faces left to right for exactly that reason (insightface's own order is score-ranked and unstable between near-identical images), and a malformed box is a protocol error rather than a zero-filled struct, because a wrong box aims the swap at the wrong person. The provider is the first here to POST more than one file, so buildMultipart gained buildMultipartFiles and now delegates to it — one writer loop, so the two cannot drift in how they escape names or terminate the body. index and all are mutually exclusive ON THE WIRE: the shim ignores index under all=true, and sending both would imply a precedence the caller cannot see. A JSON body is refused rather than returned as image bytes — the shim answers JSON on a semantic miss (no face in the source), and handing that back as a picture would report success while delivering a file that is not one. |