pmedia : remove bg | crop

This commit is contained in:
babayaga 2025-08-10 15:56:47 +02:00
parent b5a9c2dbea
commit e7faf3ea02
36 changed files with 4278 additions and 87 deletions

View 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>;

View 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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

View File

@ -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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

View 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>;

View 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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

File diff suppressed because one or more lines are too long

View 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[][]>;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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>;

View 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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

File diff suppressed because one or more lines are too long

View File

@ -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';

View File

@ -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=

View File

@ -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';

View 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.

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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\"",

View 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)

View File

@ -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);
}

View 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)

View File

@ -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')

View 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`);
}
};

View File

@ -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
});

View 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 }
);
}

View File

@ -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

View File

@ -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;

View File

@ -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'

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 KiB

After

Width:  |  Height:  |  Size: 649 KiB

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB