pmedia : remove bg | crop
6
packages/media/dist-in/commands/background-remove-bria.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const command = "background:remove:bria";
|
||||
export declare const desc = "Remove background from images using Bria AI";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
77
packages/media/dist-in/commands/background-remove-bria.js
Normal file
@ -0,0 +1,77 @@
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons';
|
||||
import { logger } from '../index.js';
|
||||
import { cli } from '../cli.js';
|
||||
import { sanitize, defaults } from '../_cli.js';
|
||||
import { briaBackgroundRemove } from '../lib/media/images/background-remove-bria.js';
|
||||
export const defaultOptions = (yargs) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('apiKey', {
|
||||
describe: 'Bria API key (or set in config.bria.key)',
|
||||
type: 'string'
|
||||
}).option('sync', {
|
||||
describe: 'Use synchronous processing (recommended)',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('contentModeration', {
|
||||
describe: 'Enable content moderation',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('preserveAlpha', {
|
||||
describe: 'Preserve alpha channel from input image',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
});
|
||||
};
|
||||
export const command = 'background:remove:bria';
|
||||
export const desc = 'Remove background from images using Bria AI';
|
||||
export const builder = defaultOptions;
|
||||
export async function handler(argv) {
|
||||
defaults();
|
||||
const options = sanitize(argv);
|
||||
logger.settings.minLevel = options.logLevel;
|
||||
const config = CONFIG_DEFAULT();
|
||||
// Get API key from argument, environment variable, or config
|
||||
options.apiKey = options.apiKey || process.env.BRIA_API_TOKEN || config?.bria?.key;
|
||||
if (!options.apiKey) {
|
||||
logger.error('Bria API key is required. Provide it via --apiKey argument, set BRIA_API_TOKEN environment variable, or configure in config.bria.key');
|
||||
logger.info('Get your API key at: https://docs.bria.ai/');
|
||||
process.exit(1);
|
||||
}
|
||||
// Map CLI arguments to library options (using same names)
|
||||
options.sync = argv.sync;
|
||||
options.contentModeration = argv.contentModeration;
|
||||
options.preserveAlpha = argv.preserveAlpha;
|
||||
logger.info("Removing background with Bria AI options:", {
|
||||
sync: options.sync,
|
||||
contentModeration: options.contentModeration,
|
||||
preserveAlpha: options.preserveAlpha,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
await briaBackgroundRemove(options);
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1yZW1vdmUtYnJpYS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb21tYW5kcy9iYWNrZ3JvdW5kLXJlbW92ZS1icmlhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNsRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDL0IsT0FBTyxFQUNILFFBQVEsRUFDUixRQUFRLEVBQ1gsTUFBTSxZQUFZLENBQUE7QUFFbkIsT0FBTyxFQUNILG9CQUFvQixFQUV2QixNQUFNLCtDQUErQyxDQUFBO0FBRXRELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWUsRUFBRSxFQUFFO0lBQzlDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtRQUM1QixZQUFZLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLFFBQVEsRUFBRSxrQkFBa0I7S0FDL0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSxnQ0FBZ0M7UUFDMUMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx5Q0FBeUM7UUFDbkQsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIs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
|
||||
@ -34,21 +34,21 @@ export const defaultOptions = (yargs) => {
|
||||
type: 'string'
|
||||
}).option('model', {
|
||||
describe: 'Background removal model to use',
|
||||
choices: ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta'],
|
||||
default: 'u2net'
|
||||
}).option('alphaMattting', {
|
||||
default: 'lucataco/remove-bg:95fcc2a26d3899cd6c2691c900465aaeff466285a65c14638cc5f36f34befaf1',
|
||||
type: 'string'
|
||||
}).option('alpha_matting', {
|
||||
describe: 'Enable alpha matting for better edge refinement',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('alphaMattingForegroundThreshold', {
|
||||
}).option('alpha_matting_foreground_threshold', {
|
||||
describe: 'Alpha matting foreground threshold',
|
||||
type: 'number',
|
||||
default: 270
|
||||
}).option('alphaMattingBackgroundThreshold', {
|
||||
}).option('alpha_matting_background_threshold', {
|
||||
describe: 'Alpha matting background threshold',
|
||||
type: 'number',
|
||||
default: 10
|
||||
}).option('alphaMattingErodeSize', {
|
||||
}).option('alpha_matting_erode_size', {
|
||||
describe: 'Alpha matting erode size',
|
||||
type: 'number',
|
||||
default: 10
|
||||
@ -69,12 +69,12 @@ export async function handler(argv) {
|
||||
logger.info('Get your API key at: https://replicate.com/account/api-tokens');
|
||||
process.exit(1);
|
||||
}
|
||||
// Map CLI arguments to library options
|
||||
// Map CLI arguments to library options (using same names)
|
||||
options.model = argv.model;
|
||||
options.alpha_matting = argv.alphaMattting;
|
||||
options.alpha_matting_foreground_threshold = argv.alphaMattingForegroundThreshold;
|
||||
options.alpha_matting_background_threshold = argv.alphaMattingBackgroundThreshold;
|
||||
options.alpha_matting_erode_size = argv.alphaMattingErodeSize;
|
||||
options.alpha_matting = argv.alpha_matting;
|
||||
options.alpha_matting_foreground_threshold = argv.alpha_matting_foreground_threshold;
|
||||
options.alpha_matting_background_threshold = argv.alpha_matting_background_threshold;
|
||||
options.alpha_matting_erode_size = argv.alpha_matting_erode_size;
|
||||
logger.info("Removing background with options:", {
|
||||
model: options.model,
|
||||
alpha_matting: options.alpha_matting,
|
||||
@ -83,4 +83,4 @@ export async function handler(argv) {
|
||||
await backgroundRemove(options);
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1yZW1vdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29tbWFuZHMvYmFja2dyb3VuZC1yZW1vdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBQ2xELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDcEMsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUMvQixPQUFPLEVBQ0gsUUFBUSxFQUNSLFFBQVEsRUFDWCxNQUFNLFlBQVksQ0FBQTtBQUVuQixPQUFPLEVBQ0gsZ0JBQWdCLEVBRW5CLE1BQU0sMENBQTBDLENBQUE7QUFFakQsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBZSxFQUFFLEVBQUU7SUFDOUMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUN2QixRQUFRLEVBQUUsa0JBQWtCO1FBQzVCLFlBQVksRUFBRSxJQUFJO0tBQ3JCLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2IsUUFBUSxFQUFFLGtCQUFrQjtLQUMvQixDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNmLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLGdDQUFnQztRQUMxQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHlDQUF5QztRQUNuRCxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHdCQUF3QjtRQUNsQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixPQUFPLEVBQUUs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
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1yZW1vdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29tbWFuZHMvYmFja2dyb3VuZC1yZW1vdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBQ2xELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDcEMsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUMvQixPQUFPLEVBQ0gsUUFBUSxFQUNSLFFBQVEsRUFDWCxNQUFNLFlBQVksQ0FBQTtBQUluQixPQUFPLEVBQ0gsZ0JBQWdCLEVBRW5CLE1BQU0sMENBQTBDLENBQUE7QUFFakQsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBZSxFQUFFLEVBQUU7SUFDOUMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUN2QixRQUFRLEVBQUUsa0JBQWtCO1FBQzVCLFlBQVksRUFBRSxJQUFJO0tBQ3JCLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2IsUUFBUSxFQUFFLGtCQUFrQjtLQUMvQixDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNmLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLGdDQUFnQztRQUMxQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHlDQUF5QztRQUNuRCxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHdCQUF3QjtRQUNsQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixPQUFPLEVBQUUs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
|
||||
6
packages/media/dist-in/commands/crop-foreground.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const command = "image-crop:foreground";
|
||||
export declare const desc = "Crop foreground from images using Bria AI";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
77
packages/media/dist-in/commands/crop-foreground.js
Normal file
@ -0,0 +1,77 @@
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons';
|
||||
import { logger } from '../index.js';
|
||||
import { cli } from '../cli.js';
|
||||
import { sanitize, defaults } from '../_cli.js';
|
||||
import { cropForeground } from '../lib/media/images/crop-foreground.js';
|
||||
export const defaultOptions = (yargs) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('apiKey', {
|
||||
describe: 'Bria API key (or set in config.bria.key)',
|
||||
type: 'string'
|
||||
}).option('sync', {
|
||||
describe: 'Use synchronous processing (recommended)',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('contentModeration', {
|
||||
describe: 'Enable content moderation',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('preserveAlpha', {
|
||||
describe: 'Preserve alpha channel from input image',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
});
|
||||
};
|
||||
export const command = 'image-crop:foreground';
|
||||
export const desc = 'Crop foreground from images using Bria AI';
|
||||
export const builder = defaultOptions;
|
||||
export async function handler(argv) {
|
||||
defaults();
|
||||
const options = sanitize(argv);
|
||||
logger.settings.minLevel = options.logLevel;
|
||||
const config = CONFIG_DEFAULT();
|
||||
// Get API key from argument, environment variable, or config
|
||||
options.apiKey = options.apiKey || process.env.BRIA_API_KEY || config?.bria?.key;
|
||||
if (!options.apiKey) {
|
||||
logger.error('Bria API key is required. Provide it via --apiKey argument, set BRIA_API_KEY environment variable, or configure it in config.bria.key');
|
||||
logger.info('Get your API key at: https://bria.ai/');
|
||||
process.exit(1);
|
||||
}
|
||||
// Map CLI arguments to library options
|
||||
options.sync = argv.sync;
|
||||
options.contentModeration = argv.contentModeration;
|
||||
options.preserveAlpha = argv.preserveAlpha;
|
||||
logger.info("Cropping foreground with Bria AI options:", {
|
||||
sync: options.sync,
|
||||
contentModeration: options.contentModeration,
|
||||
preserveAlpha: options.preserveAlpha,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
await cropForeground(options);
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JvcC1mb3JlZ3JvdW5kLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL2Nyb3AtZm9yZWdyb3VuZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDbEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUNwQyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQy9CLE9BQU8sRUFDSCxRQUFRLEVBQ1IsUUFBUSxFQUNYLE1BQU0sWUFBWSxDQUFBO0FBRW5CLE9BQU8sRUFDSCxjQUFjLEVBRWpCLE1BQU0sd0NBQXdDLENBQUE7QUFFL0MsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBZSxFQUFFLEVBQUU7SUFDOUMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUN2QixRQUFRLEVBQUUsa0JBQWtCO1FBQzVCLFlBQVksRUFBRSxJQUFJO0tBQ3JCLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2IsUUFBUSxFQUFFLGtCQUFrQjtLQUMvQixDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNmLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLGdDQUFnQztRQUMxQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHlDQUF5QztRQUNuRCxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHdCQUF3QjtRQUNsQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixPQUFPLEVBQUUsS0FBSztR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
|
||||
9
packages/media/dist-in/lib/media/images/background-remove-bria.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
import { IOptions } from '../../../types.js';
|
||||
export interface BriaBackgroundRemoveOptions extends IOptions {
|
||||
apiKey?: string;
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
}
|
||||
export declare function removeBriaBackground(inputPath: string, outputPath: string, options: BriaBackgroundRemoveOptions): Promise<void>;
|
||||
export declare const briaBackgroundRemove: (options: BriaBackgroundRemoveOptions) => Promise<void[][]>;
|
||||
8
packages/media/dist-in/lib/media/images/crop-foreground.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import { IOptions } from '../../../types.js';
|
||||
export interface CropForegroundOptions extends IOptions {
|
||||
apiKey: string;
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
}
|
||||
export declare function cropForeground(options: CropForegroundOptions): Promise<void>;
|
||||
95
packages/media/dist-in/lib/media/images/crop-foreground.js
Normal file
@ -0,0 +1,95 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import pMap from 'p-map';
|
||||
import { logger } from '../../../index.js';
|
||||
import { sync as mkdir } from '@polymech/fs/dir';
|
||||
import { targets } from '../../index.js';
|
||||
// Read image file as buffer for Bria API
|
||||
function readImageFile(filePath) {
|
||||
return fs.readFileSync(filePath);
|
||||
}
|
||||
// Download image from URL and save to file
|
||||
async function downloadImageFromUrl(imageUrl, outputPath) {
|
||||
const response = await fetch(imageUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download image: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
// Ensure output directory exists
|
||||
mkdir(path.dirname(outputPath));
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
}
|
||||
// Crop foreground from a single image using Bria API
|
||||
async function cropForegroundFile(inputPath, outputPath, options) {
|
||||
try {
|
||||
if (!options.apiKey) {
|
||||
throw new Error('Bria API key is required. Set it via --apiKey or config.bria.key');
|
||||
}
|
||||
logger.debug(`Cropping foreground from ${inputPath} using Bria AI`);
|
||||
// Read image file and encode as base64
|
||||
const imageBuffer = readImageFile(inputPath);
|
||||
const base64Image = imageBuffer.toString('base64');
|
||||
// Prepare JSON payload for Bria API
|
||||
const payload = {
|
||||
file: base64Image,
|
||||
sync: options.sync !== false,
|
||||
content_moderation: options.contentModeration || false,
|
||||
preserve_alpha: options.preserveAlpha !== false
|
||||
};
|
||||
// Call Bria AI crop foreground API
|
||||
const response = await fetch('https://engine.prod.bria-api.com/v1/crop', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'api_token': options.apiKey
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Bria API error: ${response.status} ${response.statusText} - ${errorText}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
logger.debug(`Bria API response:`, result);
|
||||
// Handle the response
|
||||
if (result.result_url || result.image_res) {
|
||||
// Download the processed image (Bria API uses result_url)
|
||||
const imageUrl = result.result_url || result.image_res;
|
||||
await downloadImageFromUrl(imageUrl, outputPath);
|
||||
logger.info(`Foreground cropped: ${inputPath} → ${outputPath}`);
|
||||
}
|
||||
else {
|
||||
throw new Error('No image result returned from Bria API');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
logger.error(`Failed to crop foreground from ${inputPath} using Bria:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// Main function to crop foreground from multiple images
|
||||
export async function cropForeground(options) {
|
||||
if (options.dry) {
|
||||
logger.info(`[DRY RUN] Would crop foreground using Bria AI: ${options.srcInfo?.FILES?.length || 0} files`);
|
||||
return;
|
||||
}
|
||||
if (!options.srcInfo?.FILES?.length) {
|
||||
logger.warn('No input files found');
|
||||
return;
|
||||
}
|
||||
logger.info(`Cropping foreground from ${options.srcInfo.FILES.length} files using Bria AI`);
|
||||
await pMap(options.srcInfo.FILES, async (src) => {
|
||||
const dstAll = targets(src, options);
|
||||
logger.info(`Cropping foreground ${src} to`, dstAll);
|
||||
await pMap(dstAll, async (dst) => {
|
||||
logger.debug(`Cropping foreground ${src} to ${dst} using Bria AI`);
|
||||
if (options.dry) {
|
||||
logger.info(`[DRY RUN] Would crop foreground using Bria AI: ${src} → ${dst}`);
|
||||
return;
|
||||
}
|
||||
await cropForegroundFile(src, dst, options);
|
||||
}, { concurrency: 1 });
|
||||
}, { concurrency: 2 });
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JvcC1mb3JlZ3JvdW5kLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9tZWRpYS9pbWFnZXMvY3JvcC1mb3JlZ3JvdW5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sSUFBSSxNQUFNLE9BQU8sQ0FBQztBQUN6QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDM0MsT0FBTyxFQUFFLElBQUksSUFBSSxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVqRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFTekMseUNBQXlDO0FBQ3pDLFNBQVMsYUFBYSxDQUFDLFFBQWdCO0lBQ3JDLE9BQU8sRUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUNuQyxDQUFDO0FBRUQsMkNBQTJDO0FBQzNDLEtBQUssVUFBVSxvQkFBb0IsQ0FBQyxRQUFnQixFQUFFLFVBQWtCO0lBQ3RFLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3ZDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsUUFBUSxDQUFDLE1BQU0sSUFBSSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUN6RixDQUFDO0lBRUQsaUNBQWlDO0lBQ2pDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7SUFFaEMsTUFBTSxXQUFXLEdBQUcsTUFBTSxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDakQsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FB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
|
||||
2
packages/media/dist-in/main.d.ts
vendored
@ -3,3 +3,5 @@ import './commands/resize.js';
|
||||
import './commands/pdf2jpg.js';
|
||||
import './commands/watermark.js';
|
||||
import './commands/background-remove.js';
|
||||
import './commands/background-remove-bria.js';
|
||||
import './commands/crop-foreground.js';
|
||||
|
||||
@ -6,6 +6,8 @@ import './commands/resize.js';
|
||||
import './commands/pdf2jpg.js';
|
||||
import './commands/watermark.js';
|
||||
import './commands/background-remove.js';
|
||||
import './commands/background-remove-bria.js';
|
||||
import './commands/crop-foreground.js';
|
||||
const argv = cli.argv;
|
||||
if (argv.h || argv.help) {
|
||||
cli.showHelp();
|
||||
@ -14,4 +16,4 @@ if (argv.h || argv.help) {
|
||||
else if (argv.v || argv.version) {
|
||||
process.exit();
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQUMsUUFBUSxFQUFFLENBQUE7QUFFaEQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUU5QixPQUFPLHNCQUFzQixDQUFBO0FBQzdCLE9BQU8sdUJBQXVCLENBQUE7QUFDOUIsT0FBTyx5QkFBeUIsQ0FBQTtBQUNoQyxPQUFPLGlDQUFpQyxDQUFBO0FBRXhDLE1BQU0sSUFBSSxHQUFRLEdBQUcsQ0FBQyxJQUFJLENBQUM7QUFFM0IsSUFBSSxJQUFJLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN0QixHQUFHLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDZixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDbkIsQ0FBQztLQUFNLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDaEMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ25CLENBQUMifQ==
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQUMsUUFBUSxFQUFFLENBQUE7QUFFaEQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUU5QixPQUFPLHNCQUFzQixDQUFBO0FBQzdCLE9BQU8sdUJBQXVCLENBQUE7QUFDOUIsT0FBTyx5QkFBeUIsQ0FBQTtBQUNoQyxPQUFPLGlDQUFpQyxDQUFBO0FBQ3hDLE9BQU8sc0NBQXNDLENBQUE7QUFDN0MsT0FBTywrQkFBK0IsQ0FBQTtBQUV0QyxNQUFNLElBQUksR0FBUSxHQUFHLENBQUMsSUFBSSxDQUFDO0FBRTNCLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDdEIsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ2YsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ25CLENBQUM7S0FBTSxJQUFJLElBQUksQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2hDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNuQixDQUFDIn0=
|
||||
2
packages/media/dist-in/types.d.ts
vendored
@ -52,3 +52,5 @@ export type IConvertVideoOptions = IOptions & {
|
||||
};
|
||||
export type { WatermarkOptions, LogoWatermarkOptions, TextWatermarkOptions } from './lib/media/images/watermark.js';
|
||||
export type { BackgroundRemoveOptions } from './lib/media/images/background-remove.js';
|
||||
export type { BriaBackgroundRemoveOptions } from './lib/media/images/background-remove-bria.js';
|
||||
export type { CropForegroundOptions } from './lib/media/images/crop-foreground.js';
|
||||
|
||||
604
packages/media/docs/cli-background-remove-bria.md
Normal file
@ -0,0 +1,604 @@
|
||||
# Bria AI Background Remove CLI Documentation
|
||||
|
||||
The `pm-media background:remove:bria` command uses [Bria AI's professional-grade background removal service](https://docs.bria.ai/image-editing/endpoints/background-remove) for enterprise-quality results. Bria AI offers advanced features including content moderation, alpha channel preservation, and high-precision edge detection.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Setup](#setup)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [API Usage](#api-usage)
|
||||
- [Examples](#examples)
|
||||
- [Bria AI vs Replicate](#bria-ai-vs-replicate)
|
||||
- [Advanced Features](#advanced-features)
|
||||
- [Performance Tips](#performance-tips)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @polymech/media
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Get Bria AI API Key
|
||||
|
||||
1. Visit [Bria AI API Documentation](https://docs.bria.ai/)
|
||||
2. Register for an account
|
||||
3. Get your API token from the dashboard
|
||||
4. Copy your API token
|
||||
|
||||
### 2. Set API Key
|
||||
|
||||
You can provide your API key in multiple ways:
|
||||
|
||||
**Option A: Configuration File (Recommended)**
|
||||
```javascript
|
||||
// In your config
|
||||
{
|
||||
bria: {
|
||||
key: "your_bria_api_token_here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Option B: Environment Variable**
|
||||
```bash
|
||||
export BRIA_API_TOKEN="your_bria_api_token_here"
|
||||
```
|
||||
|
||||
**Option C: Command Line Argument**
|
||||
```bash
|
||||
pm-media background:remove:bria --src input.jpg --apiKey "your_bria_api_token_here"
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
pm-media background:remove:bria --src <input> --dst <output> [options]
|
||||
```
|
||||
|
||||
### Required Parameters
|
||||
|
||||
- `--src`: Source files (FILE|FOLDER|GLOB pattern)
|
||||
|
||||
### Optional Parameters
|
||||
|
||||
- `--dst`: Destination path (defaults to source location with modified name)
|
||||
- `--apiKey`: Bria API key (if not set via config or environment variable)
|
||||
|
||||
## Command Line Options
|
||||
|
||||
### Core Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--src` | string | required | Source files (FILE\|FOLDER\|GLOB) |
|
||||
| `--dst` | string | - | Destination path |
|
||||
| `--apiKey` | string | - | Bria API key |
|
||||
|
||||
### Bria AI Specific Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--sync` | boolean | true | Use synchronous processing |
|
||||
| `--contentModeration` | boolean | false | Enable content moderation |
|
||||
| `--preserveAlpha` | boolean | true | Preserve alpha channel from input |
|
||||
|
||||
### Utility Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--dry` | boolean | false | Preview mode (no API calls) |
|
||||
| `--verbose` | boolean | false | Show detailed processing info |
|
||||
| `--debug` | boolean | false | Enable debug messages |
|
||||
| `--logLevel` | string | "info" | Log level (warn\|info\|debug\|error) |
|
||||
|
||||
## API Usage
|
||||
|
||||
### Import the Library
|
||||
|
||||
```typescript
|
||||
import {
|
||||
briaBackgroundRemove,
|
||||
removeBriaBackground,
|
||||
BriaBackgroundRemoveOptions
|
||||
} from '@polymech/media';
|
||||
```
|
||||
|
||||
### Single File Background Removal
|
||||
|
||||
```typescript
|
||||
import { removeBriaBackground } from '@polymech/media';
|
||||
|
||||
await removeBriaBackground(
|
||||
'input.jpg',
|
||||
'output.png',
|
||||
{
|
||||
apiKey: 'your_bria_api_token',
|
||||
sync: true,
|
||||
contentModeration: false,
|
||||
preserveAlpha: true
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Batch Processing API
|
||||
|
||||
```typescript
|
||||
import { briaBackgroundRemove } from '@polymech/media';
|
||||
|
||||
const options: BriaBackgroundRemoveOptions = {
|
||||
src: 'photos/*.jpg',
|
||||
dst: 'no-bg/',
|
||||
apiKey: process.env.BRIA_API_TOKEN,
|
||||
sync: true,
|
||||
contentModeration: true,
|
||||
preserveAlpha: true,
|
||||
// ... other IOptions
|
||||
};
|
||||
|
||||
await briaBackgroundRemove(options);
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
```typescript
|
||||
const enterpriseOptions: BriaBackgroundRemoveOptions = {
|
||||
src: 'professional/*.jpg',
|
||||
dst: 'processed/',
|
||||
apiKey: 'your_token',
|
||||
sync: true,
|
||||
contentModeration: true, // Enterprise content filtering
|
||||
preserveAlpha: true, // Maintain transparency
|
||||
verbose: true
|
||||
};
|
||||
|
||||
await briaBackgroundRemove(enterpriseOptions);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic Background Removal
|
||||
|
||||
```bash
|
||||
# Remove background from a single image
|
||||
pm-media background:remove:bria \
|
||||
--src "photo.jpg" \
|
||||
--dst "no-background.png"
|
||||
```
|
||||
|
||||
### Example 2: Professional Batch Processing
|
||||
|
||||
```bash
|
||||
# Process multiple images with content moderation
|
||||
pm-media background:remove:bria \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "processed/" \
|
||||
--contentModeration \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 3: E-commerce Product Images
|
||||
|
||||
```bash
|
||||
# Professional product photography processing
|
||||
pm-media background:remove:bria \
|
||||
--src "products/*.jpg" \
|
||||
--dst "catalog/" \
|
||||
--preserveAlpha \
|
||||
--sync
|
||||
```
|
||||
|
||||
### Example 4: Studio Photography Workflow
|
||||
|
||||
```bash
|
||||
# High-quality portrait processing
|
||||
pm-media background:remove:bria \
|
||||
--src "studio-photos/*.jpg" \
|
||||
--dst "final/" \
|
||||
--contentModeration \
|
||||
--preserveAlpha \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 5: Safe Content Processing
|
||||
|
||||
```bash
|
||||
# Process with content moderation enabled
|
||||
pm-media background:remove:bria \
|
||||
--src "user-uploads/*.jpg" \
|
||||
--dst "moderated/" \
|
||||
--contentModeration \
|
||||
--sync
|
||||
```
|
||||
|
||||
### Example 6: Dry Run Preview
|
||||
|
||||
```bash
|
||||
# Preview what would be processed
|
||||
pm-media background:remove:bria \
|
||||
--src "batch/*.jpg" \
|
||||
--dst "output/" \
|
||||
--dry \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 7: Alpha Channel Preservation
|
||||
|
||||
```bash
|
||||
# Maintain original transparency
|
||||
pm-media background:remove:bria \
|
||||
--src "images-with-alpha/*.png" \
|
||||
--dst "processed/" \
|
||||
--preserveAlpha
|
||||
```
|
||||
|
||||
## Bria AI vs Replicate
|
||||
|
||||
### When to Use Bria AI
|
||||
|
||||
✅ **Choose Bria AI for:**
|
||||
- **Enterprise/Commercial Use**: Professional-grade service
|
||||
- **Content Moderation**: Built-in safety features
|
||||
- **Alpha Channel Preservation**: Advanced transparency handling
|
||||
- **Consistent Quality**: Enterprise-level reliability
|
||||
- **Compliance**: Content safety for business applications
|
||||
|
||||
### When to Use Replicate
|
||||
|
||||
✅ **Choose Replicate for:**
|
||||
- **Experimentation**: Multiple model options
|
||||
- **Custom Models**: Access to specialized models
|
||||
- **Alpha Matting**: Fine-tuned edge refinement
|
||||
- **Cost Optimization**: Various pricing models
|
||||
- **Flexibility**: Different AI models for different use cases
|
||||
|
||||
### Feature Comparison
|
||||
|
||||
| Feature | Bria AI | Replicate |
|
||||
|---------|---------|-----------|
|
||||
| **Quality** | Enterprise-grade | High quality |
|
||||
| **Content Moderation** | ✅ Built-in | ❌ Not available |
|
||||
| **Alpha Preservation** | ✅ Advanced | ✅ Basic |
|
||||
| **Model Selection** | ❌ Single model | ✅ Multiple models |
|
||||
| **Alpha Matting** | ❌ Not available | ✅ Available |
|
||||
| **Enterprise Support** | ✅ Yes | ❌ Community |
|
||||
| **Compliance** | ✅ Enterprise | ❌ Basic |
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Content Moderation
|
||||
|
||||
Bria AI includes built-in content moderation to ensure safe processing:
|
||||
|
||||
```bash
|
||||
# Enable content moderation for user-generated content
|
||||
pm-media background:remove:bria \
|
||||
--src "user-uploads/*.jpg" \
|
||||
--dst "safe-processed/" \
|
||||
--contentModeration \
|
||||
--verbose
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Prevents processing of inappropriate content
|
||||
- Returns detailed error messages for failed moderation
|
||||
- Suitable for public-facing applications
|
||||
- Compliant with enterprise content policies
|
||||
|
||||
### Alpha Channel Preservation
|
||||
|
||||
Advanced transparency handling for professional workflows:
|
||||
|
||||
```bash
|
||||
# Preserve original alpha channel information
|
||||
pm-media background:remove:bria \
|
||||
--src "complex-transparency/*.png" \
|
||||
--dst "processed/" \
|
||||
--preserveAlpha
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Maintains partial transparency values
|
||||
- Preserves original alpha channel data
|
||||
- Professional-quality edge handling
|
||||
- Perfect for layered compositions
|
||||
|
||||
### Synchronous vs Asynchronous Processing
|
||||
|
||||
```bash
|
||||
# Synchronous (immediate results)
|
||||
pm-media background:remove:bria --src input.jpg --dst output.png --sync
|
||||
|
||||
# Asynchronous (for large batches)
|
||||
pm-media background:remove:bria --src "batch/*.jpg" --dst "output/" --sync false
|
||||
```
|
||||
|
||||
## File Format Support
|
||||
|
||||
### Supported Input Formats
|
||||
- JPEG (.jpg, .jpeg)
|
||||
- PNG (.png)
|
||||
- WebP (.webp)
|
||||
|
||||
### Output Format
|
||||
- **PNG with transparency** (recommended)
|
||||
- High-quality background removal
|
||||
- Preserves original image quality
|
||||
|
||||
### Quality Guidelines
|
||||
|
||||
```bash
|
||||
# Best quality settings
|
||||
pm-media background:remove:bria \
|
||||
--src "high-res/*.jpg" \
|
||||
--dst "premium/" \
|
||||
--preserveAlpha \
|
||||
--sync \
|
||||
--verbose
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### 1. Use Synchronous Processing
|
||||
|
||||
```bash
|
||||
# Recommended for most use cases
|
||||
--sync true
|
||||
```
|
||||
|
||||
### 2. Batch Processing Strategy
|
||||
|
||||
```bash
|
||||
# Process in reasonable batches
|
||||
pm-media background:remove:bria --src "batch1/*.jpg" --dst "output1/"
|
||||
pm-media background:remove:bria --src "batch2/*.jpg" --dst "output2/"
|
||||
```
|
||||
|
||||
### 3. Content Moderation for Safety
|
||||
|
||||
```bash
|
||||
# Enable for user-generated content
|
||||
--contentModeration
|
||||
```
|
||||
|
||||
### 4. Alpha Channel Optimization
|
||||
|
||||
```bash
|
||||
# Only when needed (slightly slower)
|
||||
--preserveAlpha
|
||||
```
|
||||
|
||||
## Cost Considerations
|
||||
|
||||
### Bria AI Pricing
|
||||
|
||||
Background removal costs per API call. Check current pricing at [Bria AI Pricing](https://docs.bria.ai/).
|
||||
|
||||
### Cost Optimization
|
||||
|
||||
```bash
|
||||
# Test with dry run first
|
||||
pm-media background:remove:bria --src "*.jpg" --dst "output/" --dry
|
||||
|
||||
# Use content moderation to avoid processing inappropriate content
|
||||
--contentModeration
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Bria API key is required"
|
||||
```bash
|
||||
# Check configuration
|
||||
echo $BRIA_API_TOKEN
|
||||
|
||||
# Or use command line
|
||||
pm-media background:remove:bria --src input.jpg --apiKey "your_token"
|
||||
```
|
||||
|
||||
#### "API error: 422"
|
||||
- Content moderation failed
|
||||
- Image contains inappropriate content
|
||||
- Check image content and try again
|
||||
|
||||
#### "API error: 403"
|
||||
- Invalid API token
|
||||
- Insufficient permissions
|
||||
- Check your Bria AI account status
|
||||
|
||||
#### Poor edge quality
|
||||
- Bria AI automatically optimizes edges
|
||||
- Use `--preserveAlpha` for complex transparency
|
||||
- Ensure high-quality input images
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable detailed logging:
|
||||
|
||||
```bash
|
||||
pm-media background:remove:bria \
|
||||
--src "input/*" \
|
||||
--dst "output/" \
|
||||
--debug \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### API Rate Limits
|
||||
|
||||
- Monitor your API usage in Bria dashboard
|
||||
- Use appropriate batch sizes
|
||||
- Consider asynchronous processing for large batches
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### E-commerce Workflow
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Professional product photography pipeline
|
||||
|
||||
INPUT_DIR="$1"
|
||||
OUTPUT_DIR="$2"
|
||||
|
||||
# Remove backgrounds with content moderation
|
||||
pm-media background:remove:bria \
|
||||
--src "$INPUT_DIR/*.jpg" \
|
||||
--dst "$OUTPUT_DIR/no-bg/" \
|
||||
--contentModeration \
|
||||
--preserveAlpha \
|
||||
--verbose
|
||||
|
||||
# Create thumbnails
|
||||
pm-media resize \
|
||||
--src "$OUTPUT_DIR/no-bg/*.png" \
|
||||
--dst "$OUTPUT_DIR/thumbs/" \
|
||||
--width 300 \
|
||||
--height 300 \
|
||||
--fit contain \
|
||||
--background white
|
||||
```
|
||||
|
||||
### Safe Content Processing
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# User-generated content processing with safety
|
||||
|
||||
pm-media background:remove:bria \
|
||||
--src "user-uploads/*.jpg" \
|
||||
--dst "processed/" \
|
||||
--contentModeration \
|
||||
--preserveAlpha \
|
||||
--verbose
|
||||
|
||||
echo "Safe processing complete"
|
||||
```
|
||||
|
||||
### Enterprise Batch Processing
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Enterprise-grade batch processing
|
||||
|
||||
for dir in */; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "Processing directory: $dir"
|
||||
pm-media background:remove:bria \
|
||||
--src "$dir*.jpg" \
|
||||
--dst "enterprise-processed/$dir" \
|
||||
--contentModeration \
|
||||
--preserveAlpha \
|
||||
--sync \
|
||||
--verbose
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Environment Configuration
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
BRIA_API_TOKEN=your_token_here
|
||||
CONTENT_MODERATION=true
|
||||
PRESERVE_ALPHA=true
|
||||
|
||||
# Use in scripts
|
||||
pm-media background:remove:bria \
|
||||
--src "*.jpg" \
|
||||
--dst "output/" \
|
||||
--contentModeration \
|
||||
--preserveAlpha
|
||||
```
|
||||
|
||||
### Quality Control Pipeline
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Multi-stage quality control
|
||||
|
||||
INPUT="$1"
|
||||
BASE_NAME=$(basename "$INPUT" .jpg)
|
||||
|
||||
# Process with Bria AI
|
||||
pm-media background:remove:bria \
|
||||
--src "$INPUT" \
|
||||
--dst "bria-processed/${BASE_NAME}_bria.png" \
|
||||
--contentModeration \
|
||||
--preserveAlpha \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Conditional Processing
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Smart processing based on content type
|
||||
|
||||
for image in *.jpg; do
|
||||
echo "Processing $image with Bria AI..."
|
||||
|
||||
pm-media background:remove:bria \
|
||||
--src "$image" \
|
||||
--dst "processed/" \
|
||||
--contentModeration \
|
||||
--preserveAlpha
|
||||
done
|
||||
```
|
||||
|
||||
## TypeScript Definitions
|
||||
|
||||
```typescript
|
||||
interface BriaBackgroundRemoveOptions extends IOptions {
|
||||
apiKey?: string;
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
}
|
||||
|
||||
declare function removeBriaBackground(
|
||||
inputPath: string,
|
||||
outputPath: string,
|
||||
options: BriaBackgroundRemoveOptions
|
||||
): Promise<void>;
|
||||
|
||||
declare function briaBackgroundRemove(
|
||||
options: BriaBackgroundRemoveOptions
|
||||
): Promise<any>;
|
||||
```
|
||||
|
||||
## Security & Compliance
|
||||
|
||||
### API Key Security
|
||||
|
||||
- Store API keys in configuration files or environment variables
|
||||
- Never commit API keys to version control
|
||||
- Use environment-specific configurations
|
||||
- Monitor API usage in Bria dashboard
|
||||
|
||||
### Content Safety
|
||||
|
||||
- Enable content moderation for user-generated content
|
||||
- Review failed moderation results
|
||||
- Implement appropriate content policies
|
||||
- Use Bria's enterprise compliance features
|
||||
|
||||
## Contributing
|
||||
|
||||
For bug reports, feature requests, or contributions, please visit the project repository.
|
||||
|
||||
## References
|
||||
|
||||
- [Bria AI API Documentation](https://docs.bria.ai/image-editing/endpoints/background-remove)
|
||||
- [Bria AI Enterprise Features](https://docs.bria.ai/)
|
||||
- [Content Moderation Guidelines](https://docs.bria.ai/)
|
||||
|
||||
## License
|
||||
|
||||
This tool is part of the @polymech/media package. Please refer to the package license for usage terms.
|
||||
@ -85,11 +85,18 @@ Add logo or image overlays with automatic scaling and positioning.
|
||||
### Basic Image Watermark
|
||||
|
||||
```bash
|
||||
# Using explicit options
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermarkType image \
|
||||
--logoPath "logo.png"
|
||||
|
||||
# Using shortcut option (recommended)
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermark "logo.png"
|
||||
```
|
||||
|
||||
### Advanced Image Positioning
|
||||
@ -110,7 +117,8 @@ pm-media watermark \
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--logoPath` | string | required | Path to logo/watermark image file |
|
||||
| `--logoPath` | string | required | Path to logo/watermark image file (PNG, JPG, SVG) |
|
||||
| `--watermark` | string | - | Shortcut: Path to watermark file (auto-sets type to image) |
|
||||
| `--sizePct` | number | 0.2 | Size as percentage of base image width (0-1) |
|
||||
|
||||
## Command Line Options
|
||||
@ -283,15 +291,27 @@ pm-media watermark \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 5: Custom Logo Positioning
|
||||
### Example 5: SVG Logo Watermarks
|
||||
|
||||
```bash
|
||||
# High-quality SVG logo (recommended for logos)
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "branded/" \
|
||||
--watermark "company-logo.svg" \
|
||||
--position top-right \
|
||||
--sizePct 0.12 \
|
||||
--opacity 0.9
|
||||
```
|
||||
|
||||
### Example 6: Custom Logo Positioning
|
||||
|
||||
```bash
|
||||
# Small logo in top-right corner
|
||||
pm-media watermark \
|
||||
--src "social-media/*.png" \
|
||||
--dst "branded/" \
|
||||
--watermarkType image \
|
||||
--logoPath "social-logo.png" \
|
||||
--watermark "social-logo.svg" \
|
||||
--position top-right \
|
||||
--sizePct 0.08 \
|
||||
--margin 15 \
|
||||
@ -306,14 +326,57 @@ pm-media watermark \
|
||||
- WebP (.webp)
|
||||
- TIFF (.tiff, .tif)
|
||||
|
||||
### Supported Watermark Formats
|
||||
- **PNG** (.png) - With transparency support
|
||||
- **JPEG** (.jpg, .jpeg) - Standard photos
|
||||
- **SVG** (.svg) - **Recommended for logos** - Vector graphics with crisp scaling
|
||||
|
||||
### Supported Output Formats
|
||||
Output format is automatically determined by the destination file extension or input format.
|
||||
|
||||
## SVG Watermarks (Recommended for Logos)
|
||||
|
||||
### Why Use SVG for Logos?
|
||||
|
||||
✅ **Advantages of SVG watermarks:**
|
||||
- **Perfect Scaling**: Crisp at any size (vector graphics)
|
||||
- **Small File Size**: Compact file size for better performance
|
||||
- **Professional Quality**: No pixelation or quality loss
|
||||
- **Transparency Support**: Clean integration with photos
|
||||
- **Color Flexibility**: Easy to modify colors in the SVG file
|
||||
|
||||
### SVG Usage Examples
|
||||
|
||||
```bash
|
||||
# Company logo watermark
|
||||
pm-media watermark \
|
||||
--src "products/*.jpg" \
|
||||
--dst "catalog/" \
|
||||
--watermark "brand-logo.svg" \
|
||||
--sizePct 0.15
|
||||
|
||||
# Copyright watermark with custom positioning
|
||||
pm-media watermark \
|
||||
--src "portfolio/*.jpg" \
|
||||
--dst "watermarked/" \
|
||||
--watermark "copyright-symbol.svg" \
|
||||
--position bottom-left \
|
||||
--sizePct 0.08 \
|
||||
--opacity 0.7
|
||||
```
|
||||
|
||||
### Creating SVG Watermarks
|
||||
|
||||
1. **Design Tools**: Use Inkscape, Adobe Illustrator, or Figma
|
||||
2. **Keep Simple**: Avoid complex gradients for best performance
|
||||
3. **Optimize Size**: Remove unnecessary elements and metadata
|
||||
4. **Test Scaling**: Ensure logo looks good at different sizes
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Batch Processing**: Process multiple files in one command for better performance
|
||||
2. **Concurrency**: The tool processes files with controlled concurrency (default: 1)
|
||||
3. **Logo Optimization**: Use PNG logos with transparency for best results
|
||||
3. **Logo Optimization**: Use SVG for logos, PNG for complex images with transparency
|
||||
4. **Size Optimization**: Keep logo files reasonably sized to improve processing speed
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
2565
packages/media/package-lock.json
generated
@ -21,6 +21,7 @@
|
||||
"@types/fluent-ffmpeg": "^2.1.27",
|
||||
"@types/node": "^24.0.10",
|
||||
"bluebird": "^3.7.2",
|
||||
"download": "^8.0.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"glob": "^11.0.0",
|
||||
@ -36,9 +37,9 @@
|
||||
"zod": "^3.25.74"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/showdown": "^2.0.6",
|
||||
"vitest": "^3.1.1",
|
||||
"@types/glob": "^8.1.0"
|
||||
"vitest": "^3.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tsc; mocha --full-trace mocha \"spec/**/*.spec.js\"",
|
||||
|
||||
93
packages/media/src/commands/background-remove-bria.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import * as CLI from 'yargs'
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import { logger } from '../index.js'
|
||||
import { cli } from '../cli.js'
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
|
||||
import {
|
||||
briaBackgroundRemove,
|
||||
BriaBackgroundRemoveOptions
|
||||
} from '../lib/media/images/background-remove-bria.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('apiKey', {
|
||||
describe: 'Bria API key (or set in config.bria.key)',
|
||||
type: 'string'
|
||||
}).option('sync', {
|
||||
describe: 'Use synchronous processing (recommended)',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('contentModeration', {
|
||||
describe: 'Enable content moderation',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('preserveAlpha', {
|
||||
describe: 'Preserve alpha channel from input image',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'background:remove:bria';
|
||||
export const desc = 'Remove background from images using Bria AI';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv) as BriaBackgroundRemoveOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
const config: any = CONFIG_DEFAULT()
|
||||
|
||||
// Get API key from argument, environment variable, or config
|
||||
options.apiKey = options.apiKey || process.env.BRIA_API_TOKEN || config?.bria?.key;
|
||||
|
||||
if (!options.apiKey) {
|
||||
logger.error('Bria API key is required. Provide it via --apiKey argument, set BRIA_API_TOKEN environment variable, or configure in config.bria.key');
|
||||
logger.info('Get your API key at: https://docs.bria.ai/');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Map CLI arguments to library options (using same names)
|
||||
options.sync = argv.sync as boolean;
|
||||
options.contentModeration = argv.contentModeration as boolean;
|
||||
options.preserveAlpha = argv.preserveAlpha as boolean;
|
||||
|
||||
logger.info("Removing background with Bria AI options:", {
|
||||
sync: options.sync,
|
||||
contentModeration: options.contentModeration,
|
||||
preserveAlpha: options.preserveAlpha,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
|
||||
await briaBackgroundRemove(options);
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
@ -7,6 +7,8 @@ import {
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
|
||||
import download from 'download'
|
||||
|
||||
import {
|
||||
backgroundRemove,
|
||||
BackgroundRemoveOptions
|
||||
@ -43,21 +45,21 @@ export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
type: 'string'
|
||||
}).option('model', {
|
||||
describe: 'Background removal model to use',
|
||||
choices: ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta'],
|
||||
default: 'u2net'
|
||||
}).option('alphaMattting', {
|
||||
default: 'lucataco/remove-bg:95fcc2a26d3899cd6c2691c900465aaeff466285a65c14638cc5f36f34befaf1',
|
||||
type: 'string'
|
||||
}).option('alpha_matting', {
|
||||
describe: 'Enable alpha matting for better edge refinement',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('alphaMattingForegroundThreshold', {
|
||||
}).option('alpha_matting_foreground_threshold', {
|
||||
describe: 'Alpha matting foreground threshold',
|
||||
type: 'number',
|
||||
default: 270
|
||||
}).option('alphaMattingBackgroundThreshold', {
|
||||
describe: 'Alpha matting background threshold',
|
||||
}).option('alpha_matting_background_threshold', {
|
||||
describe: 'Alpha matting background threshold',
|
||||
type: 'number',
|
||||
default: 10
|
||||
}).option('alphaMattingErodeSize', {
|
||||
}).option('alpha_matting_erode_size', {
|
||||
describe: 'Alpha matting erode size',
|
||||
type: 'number',
|
||||
default: 10
|
||||
@ -76,26 +78,26 @@ export async function handler(argv: CLI.Arguments) {
|
||||
|
||||
// Get API key from argument or environment variable
|
||||
options.apiKey = options.apiKey || process.env.REPLICATE_API_TOKEN || config?.replicate?.key;
|
||||
|
||||
|
||||
if (!options.apiKey) {
|
||||
logger.error('Replicate API key is required. Provide it via --apiKey argument or set REPLICATE_API_TOKEN environment variable');
|
||||
logger.info('Get your API key at: https://replicate.com/account/api-tokens');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Map CLI arguments to library options
|
||||
|
||||
// Map CLI arguments to library options (using same names)
|
||||
options.model = argv.model as string;
|
||||
options.alpha_matting = argv.alphaMattting as boolean;
|
||||
options.alpha_matting_foreground_threshold = argv.alphaMattingForegroundThreshold as number;
|
||||
options.alpha_matting_background_threshold = argv.alphaMattingBackgroundThreshold as number;
|
||||
options.alpha_matting_erode_size = argv.alphaMattingErodeSize as number;
|
||||
|
||||
options.alpha_matting = argv.alpha_matting as boolean;
|
||||
options.alpha_matting_foreground_threshold = argv.alpha_matting_foreground_threshold as number;
|
||||
options.alpha_matting_background_threshold = argv.alpha_matting_background_threshold as number;
|
||||
options.alpha_matting_erode_size = argv.alpha_matting_erode_size as number;
|
||||
|
||||
logger.info("Removing background with options:", {
|
||||
model: options.model,
|
||||
alpha_matting: options.alpha_matting,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
|
||||
|
||||
await backgroundRemove(options);
|
||||
}
|
||||
|
||||
|
||||
93
packages/media/src/commands/crop-foreground.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import * as CLI from 'yargs'
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import { logger } from '../index.js'
|
||||
import { cli } from '../cli.js'
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
|
||||
import {
|
||||
cropForeground,
|
||||
CropForegroundOptions
|
||||
} from '../lib/media/images/crop-foreground.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('apiKey', {
|
||||
describe: 'Bria API key (or set in config.bria.key)',
|
||||
type: 'string'
|
||||
}).option('sync', {
|
||||
describe: 'Use synchronous processing (recommended)',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('contentModeration', {
|
||||
describe: 'Enable content moderation',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('preserveAlpha', {
|
||||
describe: 'Preserve alpha channel from input image',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'image-crop:foreground';
|
||||
export const desc = 'Crop foreground from images using Bria AI';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv) as CropForegroundOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
const config: any = CONFIG_DEFAULT()
|
||||
|
||||
// Get API key from argument, environment variable, or config
|
||||
options.apiKey = options.apiKey || process.env.BRIA_API_KEY || config?.bria?.key;
|
||||
|
||||
if (!options.apiKey) {
|
||||
logger.error('Bria API key is required. Provide it via --apiKey argument, set BRIA_API_KEY environment variable, or configure it in config.bria.key');
|
||||
logger.info('Get your API key at: https://bria.ai/');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Map CLI arguments to library options
|
||||
options.sync = argv.sync as boolean;
|
||||
options.contentModeration = argv.contentModeration as boolean;
|
||||
options.preserveAlpha = argv.preserveAlpha as boolean;
|
||||
|
||||
logger.info("Cropping foreground with Bria AI options:", {
|
||||
sync: options.sync,
|
||||
contentModeration: options.contentModeration,
|
||||
preserveAlpha: options.preserveAlpha,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
|
||||
await cropForeground(options);
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
@ -43,13 +43,15 @@ export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
default: 'info'
|
||||
}).option('watermarkType', {
|
||||
describe: 'Type of watermark: text or image',
|
||||
choices: ['text', 'image'],
|
||||
demandOption: true
|
||||
choices: ['text', 'image']
|
||||
}).option('text', {
|
||||
describe: 'Text to use for text watermark',
|
||||
type: 'string'
|
||||
}).option('logoPath', {
|
||||
describe: 'Path to logo image for image watermark',
|
||||
describe: 'Path to logo image for image watermark (PNG, JPG, SVG)',
|
||||
type: 'string'
|
||||
}).option('watermark', {
|
||||
describe: 'Shortcut: Path to watermark image file (automatically sets type to image)',
|
||||
type: 'string'
|
||||
}).option('position', {
|
||||
describe: 'Position of watermark',
|
||||
@ -101,6 +103,24 @@ export async function handler(argv: CLI.Arguments) {
|
||||
const options = sanitize(argv) as WatermarkOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
|
||||
// Handle --watermark shortcut option
|
||||
if (argv.watermark) {
|
||||
options.watermarkType = 'image';
|
||||
options.logoPath = argv.watermark as string;
|
||||
}
|
||||
|
||||
// Auto-detect watermark type if not specified
|
||||
if (!options.watermarkType) {
|
||||
if (options.text) {
|
||||
options.watermarkType = 'text';
|
||||
} else if (options.logoPath || argv.watermark) {
|
||||
options.watermarkType = 'image';
|
||||
} else {
|
||||
logger.error('Must specify either --text, --logoPath, or --watermark option');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate required options based on watermark type
|
||||
if (options.watermarkType === 'text' && !options.text) {
|
||||
logger.error('Text is required when using text watermark type')
|
||||
|
||||
135
packages/media/src/lib/media/images/background-remove-bria.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import pMap from 'p-map';
|
||||
import { logger } from '../../../index.js';
|
||||
import { IOptions } from '../../../types.js';
|
||||
import { targets } from '../../index.js';
|
||||
import { sync as mkdir } from '@polymech/fs/dir';
|
||||
|
||||
export interface BriaBackgroundRemoveOptions extends IOptions {
|
||||
apiKey?: string;
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
}
|
||||
|
||||
// Read image file as buffer for Bria API
|
||||
function readImageFile(filePath: string): Buffer {
|
||||
return fs.readFileSync(filePath);
|
||||
}
|
||||
|
||||
// Download image from URL and save to file
|
||||
async function downloadImageFromUrl(imageUrl: string, outputPath: string): Promise<void> {
|
||||
const response = await fetch(imageUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download image: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
|
||||
// Ensure output directory exists
|
||||
mkdir(path.dirname(outputPath));
|
||||
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
}
|
||||
|
||||
export async function removeBriaBackground(
|
||||
inputPath: string,
|
||||
outputPath: string,
|
||||
options: BriaBackgroundRemoveOptions
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!options.apiKey) {
|
||||
throw new Error('Bria API key is required. Set it via --apiKey or config.bria.key');
|
||||
}
|
||||
|
||||
logger.debug(`Removing background from ${inputPath} using Bria AI`);
|
||||
|
||||
// Read image file as buffer
|
||||
const imageBuffer = readImageFile(inputPath);
|
||||
const fileName = path.basename(inputPath);
|
||||
|
||||
// Prepare form data for Bria API
|
||||
const formData = new FormData();
|
||||
|
||||
// Create a Blob from the image buffer with proper MIME type
|
||||
const imageBlob = new Blob([imageBuffer], {
|
||||
type: `image/${path.extname(inputPath).slice(1).toLowerCase()}`
|
||||
});
|
||||
|
||||
// Add the image file as Blob
|
||||
formData.append('file', imageBlob, fileName);
|
||||
|
||||
// Add options
|
||||
formData.append('sync', String(options.sync !== false));
|
||||
formData.append('content_moderation', String(options.contentModeration || false));
|
||||
formData.append('preserve_alpha', String(options.preserveAlpha !== false));
|
||||
|
||||
// Call Bria AI background removal API
|
||||
const response = await fetch('https://engine.prod.bria-api.com/v1/background/remove', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'api_token': options.apiKey
|
||||
// Don't set Content-Type, let fetch set it for FormData
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Bria API error: ${response.status} ${response.statusText} - ${errorText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
logger.debug(`Bria API response:`, result);
|
||||
|
||||
// Handle the response
|
||||
if (result.result_url || result.image_res) {
|
||||
// Download the processed image (Bria API uses result_url)
|
||||
const imageUrl = result.result_url || result.image_res;
|
||||
await downloadImageFromUrl(imageUrl, outputPath);
|
||||
logger.info(`Background removed: ${inputPath} → ${outputPath}`);
|
||||
} else {
|
||||
throw new Error('No image result returned from Bria API');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Failed to remove background from ${inputPath} using Bria:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const _briaBackgroundRemove = async (
|
||||
file: string,
|
||||
targets: string[],
|
||||
onNode: (data: any) => void = () => {},
|
||||
options: BriaBackgroundRemoveOptions
|
||||
) => {
|
||||
return pMap(targets, async (target) => {
|
||||
options.verbose && logger.debug(`Removing background ${file} to ${target} using Bria AI`);
|
||||
if (options.dry) {
|
||||
logger.info(`[DRY RUN] Would remove background using Bria AI: ${file} → ${target}`);
|
||||
return;
|
||||
}
|
||||
return await removeBriaBackground(file, target, options);
|
||||
}, { concurrency: 1 });
|
||||
};
|
||||
|
||||
export const briaBackgroundRemove = async (options: BriaBackgroundRemoveOptions) => {
|
||||
// reporting, stub
|
||||
let reports: any = [];
|
||||
const onNode = (data: any) => reports.push(data);
|
||||
|
||||
if (options.srcInfo) {
|
||||
options.verbose && logger.info(`Removing background from ${options.srcInfo.FILES.length} files using Bria AI`);
|
||||
return await pMap(options.srcInfo.FILES, async (f) => {
|
||||
const outputs = targets(f, options);
|
||||
options.verbose && logger.info(`Removing background ${f} to`, outputs);
|
||||
return _briaBackgroundRemove(f, outputs, onNode, options);
|
||||
}, { concurrency: 1 });
|
||||
} else {
|
||||
options.debug && logger.error(`Invalid source info`);
|
||||
}
|
||||
};
|
||||
@ -75,23 +75,23 @@ export async function removeBackgroundFile(
|
||||
auth: options.apiKey,
|
||||
});
|
||||
|
||||
logger.debug(`Removing background from ${inputPath}`);
|
||||
|
||||
|
||||
// Encode input image to base64 data URL
|
||||
const inputImageBase64 = encodeImageToBase64(inputPath);
|
||||
|
||||
|
||||
// Prepare input for the model
|
||||
const input = {
|
||||
image: inputImageBase64,
|
||||
model: options.model || 'u2net',
|
||||
alpha_matting: options.alpha_matting || false,
|
||||
alpha_matting_foreground_threshold: options.alpha_matting_foreground_threshold || 270,
|
||||
alpha_matting_background_threshold: options.alpha_matting_background_threshold || 10,
|
||||
alpha_matting_erode_size: options.alpha_matting_erode_size || 10,
|
||||
image: inputImageBase64,
|
||||
alpha_matting: 'alpha_matting' in options ? options.alpha_matting : false,
|
||||
alpha_matting_foreground_threshold: 'alpha_matting_foreground_threshold' in options ? options.alpha_matting_foreground_threshold : 270,
|
||||
alpha_matting_background_threshold: 'alpha_matting_background_threshold' in options ? options.alpha_matting_background_threshold : 10,
|
||||
alpha_matting_erode_size: 'alpha_matting_erode_size' in options ? options.alpha_matting_erode_size : 10,
|
||||
};
|
||||
|
||||
logger.debug(`Removing background from ${inputPath}`, options);
|
||||
|
||||
// Run the background removal model
|
||||
const output = await replicate.run("cjwbw/rembg:fb8af171cfa1616ddcf1242c093f9c46bcada5ad4cf6f2fbe8b81b330ec5c003", {
|
||||
const output = await replicate.run(options.model, {
|
||||
input
|
||||
});
|
||||
|
||||
|
||||
134
packages/media/src/lib/media/images/crop-foreground.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import pMap from 'p-map';
|
||||
import { logger } from '../../../index.js';
|
||||
import { sync as mkdir } from '@polymech/fs/dir';
|
||||
import { IOptions } from '../../../types.js';
|
||||
import { targets } from '../../index.js';
|
||||
|
||||
export interface CropForegroundOptions extends IOptions {
|
||||
apiKey: string;
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
}
|
||||
|
||||
// Read image file as buffer for Bria API
|
||||
function readImageFile(filePath: string): Buffer {
|
||||
return fs.readFileSync(filePath);
|
||||
}
|
||||
|
||||
// Download image from URL and save to file
|
||||
async function downloadImageFromUrl(imageUrl: string, outputPath: string): Promise<void> {
|
||||
const response = await fetch(imageUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download image: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Ensure output directory exists
|
||||
mkdir(path.dirname(outputPath));
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
}
|
||||
|
||||
// Crop foreground from a single image using Bria API
|
||||
async function cropForegroundFile(
|
||||
inputPath: string,
|
||||
outputPath: string,
|
||||
options: CropForegroundOptions
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!options.apiKey) {
|
||||
throw new Error('Bria API key is required. Set it via --apiKey or config.bria.key');
|
||||
}
|
||||
|
||||
logger.debug(`Cropping foreground from ${inputPath} using Bria AI`);
|
||||
|
||||
// Read image file and encode as base64
|
||||
const imageBuffer = readImageFile(inputPath);
|
||||
const base64Image = imageBuffer.toString('base64');
|
||||
|
||||
// Prepare JSON payload for Bria API
|
||||
const payload = {
|
||||
file: base64Image,
|
||||
sync: options.sync !== false,
|
||||
content_moderation: options.contentModeration || false,
|
||||
preserve_alpha: options.preserveAlpha !== false
|
||||
};
|
||||
|
||||
// Call Bria AI crop foreground API
|
||||
const response = await fetch('https://engine.prod.bria-api.com/v1/crop', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'api_token': options.apiKey
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Bria API error: ${response.status} ${response.statusText} - ${errorText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
logger.debug(`Bria API response:`, result);
|
||||
|
||||
// Handle the response
|
||||
if (result.result_url || result.image_res) {
|
||||
// Download the processed image (Bria API uses result_url)
|
||||
const imageUrl = result.result_url || result.image_res;
|
||||
await downloadImageFromUrl(imageUrl, outputPath);
|
||||
logger.info(`Foreground cropped: ${inputPath} → ${outputPath}`);
|
||||
} else {
|
||||
throw new Error('No image result returned from Bria API');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Failed to crop foreground from ${inputPath} using Bria:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Main function to crop foreground from multiple images
|
||||
export async function cropForeground(options: CropForegroundOptions): Promise<void> {
|
||||
|
||||
if (options.dry) {
|
||||
logger.info(`[DRY RUN] Would crop foreground using Bria AI: ${options.srcInfo?.FILES?.length || 0} files`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.srcInfo?.FILES?.length) {
|
||||
logger.warn('No input files found');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`Cropping foreground from ${options.srcInfo.FILES.length} files using Bria AI`);
|
||||
|
||||
await pMap(
|
||||
options.srcInfo.FILES,
|
||||
async (src: string) => {
|
||||
const dstAll = targets(src, options);
|
||||
logger.info(`Cropping foreground ${src} to`, dstAll);
|
||||
|
||||
await pMap(
|
||||
dstAll,
|
||||
async (dst: string) => {
|
||||
logger.debug(`Cropping foreground ${src} to ${dst} using Bria AI`);
|
||||
|
||||
if (options.dry) {
|
||||
logger.info(`[DRY RUN] Would crop foreground using Bria AI: ${src} → ${dst}`);
|
||||
return;
|
||||
}
|
||||
|
||||
await cropForegroundFile(src, dst, options);
|
||||
},
|
||||
{ concurrency: 1 }
|
||||
);
|
||||
},
|
||||
{ concurrency: 2 }
|
||||
);
|
||||
}
|
||||
@ -62,7 +62,22 @@ export async function addLogoWatermark(
|
||||
|
||||
// Resize logo to sizePct of base width
|
||||
const logoWidth = Math.max(1, Math.round(W * sizePct));
|
||||
const logoBuf = await sharp(logoPath).resize({ width: logoWidth }).toBuffer();
|
||||
|
||||
// Handle SVG files specially
|
||||
let logoSharp;
|
||||
const logoExt = path.extname(logoPath).toLowerCase();
|
||||
|
||||
if (logoExt === '.svg') {
|
||||
// For SVG files, specify density for better quality
|
||||
logoSharp = sharp(logoPath, {
|
||||
density: 300 // High DPI for crisp SVG rendering
|
||||
}).resize({ width: logoWidth });
|
||||
} else {
|
||||
// For other image formats
|
||||
logoSharp = sharp(logoPath).resize({ width: logoWidth });
|
||||
}
|
||||
|
||||
const logoBuf = await logoSharp.toBuffer();
|
||||
const { width: w = 0, height: h = 0 } = await sharp(logoBuf).metadata();
|
||||
|
||||
// Compute pixel placement
|
||||
|
||||
@ -7,6 +7,8 @@ import './commands/resize.js'
|
||||
import './commands/pdf2jpg.js'
|
||||
import './commands/watermark.js'
|
||||
import './commands/background-remove.js'
|
||||
import './commands/background-remove-bria.js'
|
||||
import './commands/crop-foreground.js'
|
||||
|
||||
const argv: any = cli.argv;
|
||||
|
||||
|
||||
@ -60,4 +60,8 @@ export type IConvertVideoOptions = IOptions & {
|
||||
export type { WatermarkOptions, LogoWatermarkOptions, TextWatermarkOptions } from './lib/media/images/watermark.js'
|
||||
|
||||
// Re-export background removal types
|
||||
export type { BackgroundRemoveOptions } from './lib/media/images/background-remove.js'
|
||||
export type { BackgroundRemoveOptions } from './lib/media/images/background-remove.js'
|
||||
export type { BriaBackgroundRemoveOptions } from './lib/media/images/background-remove-bria.js'
|
||||
|
||||
// Re-export crop foreground types
|
||||
export type { CropForegroundOptions } from './lib/media/images/crop-foreground.js'
|
||||
23
packages/media/tests/assets/test-logo.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ff6b6b;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#4ecdc4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background circle -->
|
||||
<circle cx="50" cy="50" r="45" fill="url(#grad1)" stroke="#fff" stroke-width="2"/>
|
||||
|
||||
<!-- Logo text -->
|
||||
<text x="50" y="35" font-family="Arial, sans-serif" font-size="12" font-weight="bold"
|
||||
text-anchor="middle" fill="white">LOGO</text>
|
||||
|
||||
<!-- Decorative element -->
|
||||
<rect x="35" y="55" width="30" height="4" rx="2" fill="white" opacity="0.8"/>
|
||||
|
||||
<!-- Small decorative dots -->
|
||||
<circle cx="40" cy="70" r="2" fill="white" opacity="0.6"/>
|
||||
<circle cx="50" cy="70" r="2" fill="white" opacity="0.8"/>
|
||||
<circle cx="60" cy="70" r="2" fill="white" opacity="0.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 961 B |
BIN
packages/media/tests/images/bg-removed/DSC05572.JPG
Normal file
|
After Width: | Height: | Size: 633 KiB |
|
Before Width: | Height: | Size: 676 KiB After Width: | Height: | Size: 649 KiB |
7
packages/media/tests/images/watermark-add/add.sh
Normal file
@ -0,0 +1,7 @@
|
||||
pm-media watermark \
|
||||
--src "*.jpg" \
|
||||
--dst "watermarked/" \
|
||||
--watermark "polymech-saw.svg" \
|
||||
--position bottom-left \
|
||||
--sizePct 0.08 \
|
||||
--opacity 0.9
|
||||
57
packages/media/tests/images/watermark-add/polymech-saw.svg
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
packages/media/tests/out/DSC01177.JPG
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
packages/media/tests/out/DSC05639.JPG
Normal file
|
After Width: | Height: | Size: 2.5 MiB |