mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-08 10:46:06 +00:00
feat(cli): add --background-output to remove-background
Emit an inverse-alpha background plate alongside the cutout in a single inference pass. Same source RGB, alpha = 255 − mask. Dual-encoder pipeline runs in parallel; both outputs share the same --quality preset. This is a hole-cut plate (subject region transparent), not an inpainted clean plate — composite something opaque under it to fill the hole. Docs and skill cover when each is the right tool. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,10 @@ export const examples: Example[] = [
|
||||
"Remove background from a single image, output transparent PNG",
|
||||
"hyperframes remove-background portrait.jpg -o cutout.png",
|
||||
],
|
||||
[
|
||||
"Separate the layers — emit both the cutout and an inverse-alpha background plate (subject region transparent)",
|
||||
"hyperframes remove-background avatar.mp4 -o subject.webm --background-output plate.webm",
|
||||
],
|
||||
[
|
||||
"Force CPU (skip CoreML/CUDA)",
|
||||
"hyperframes remove-background avatar.mp4 -o transparent.webm --device cpu",
|
||||
@@ -52,6 +56,12 @@ export default defineCommand({
|
||||
description: "Output path. Format inferred from extension: .webm (default), .mov, .png",
|
||||
alias: "o",
|
||||
},
|
||||
"background-output": {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional second output path for the inverse-alpha background plate (subject region transparent, original surroundings opaque). Hole-cut, not inpainted — composite something underneath to fill the hole. Must be .webm or .mov; not allowed for image inputs.",
|
||||
alias: "b",
|
||||
},
|
||||
device: {
|
||||
type: "string",
|
||||
description: `Execution provider: ${DEVICES.join(", ")}`,
|
||||
@@ -104,6 +114,8 @@ export default defineCommand({
|
||||
|
||||
const inputPath = resolve(args.input);
|
||||
const outputPath = resolve(args.output);
|
||||
const backgroundOutputArg = args["background-output"];
|
||||
const backgroundOutputPath = backgroundOutputArg ? resolve(backgroundOutputArg) : undefined;
|
||||
|
||||
const { render } = await import("../background-removal/pipeline.js");
|
||||
|
||||
@@ -114,6 +126,7 @@ export default defineCommand({
|
||||
const result = await render({
|
||||
inputPath,
|
||||
outputPath,
|
||||
backgroundOutputPath,
|
||||
device: args.device,
|
||||
quality: args.quality,
|
||||
onProgress: (event) => {
|
||||
@@ -137,6 +150,9 @@ export default defineCommand({
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
outputPath: result.outputPath,
|
||||
...(result.backgroundOutputPath
|
||||
? { backgroundOutputPath: result.backgroundOutputPath }
|
||||
: {}),
|
||||
framesProcessed: result.framesProcessed,
|
||||
durationSeconds: Number(result.durationSeconds.toFixed(2)),
|
||||
avgMsPerFrame: Number(result.avgMsPerFrame.toFixed(1)),
|
||||
@@ -148,9 +164,12 @@ export default defineCommand({
|
||||
const fpsThroughput = result.durationSeconds
|
||||
? (result.framesProcessed / result.durationSeconds).toFixed(1)
|
||||
: "n/a";
|
||||
const outputs = result.backgroundOutputPath
|
||||
? `${c.accent(result.outputPath)} + ${c.accent(result.backgroundOutputPath)}`
|
||||
: c.accent(result.outputPath);
|
||||
spin?.stop(
|
||||
c.success(
|
||||
`Removed background from ${c.accent(String(result.framesProcessed))} frames in ${result.durationSeconds.toFixed(1)}s (${fpsThroughput} fps, ${c.accent(result.provider)}) → ${c.accent(result.outputPath)}`,
|
||||
`Removed background from ${c.accent(String(result.framesProcessed))} frames in ${result.durationSeconds.toFixed(1)}s (${fpsThroughput} fps, ${c.accent(result.provider)}) → ${outputs}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user