Advanced Features
Advanced capabilities and features of Qwen Image Edit
Advanced Features
Explore the advanced capabilities of Qwen Image Edit for power users and enterprise applications.
Batch Processing
Processing Multiple Images
Process multiple images efficiently with batch operations:
import { QwenImageEdit } from '@qwen/image-edit';
const client = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY
});
// Batch edit multiple images
const batchEdit = async (images, prompt) => {
const results = await Promise.allSettled(
images.map(async (image, index) => {
try {
const result = await client.edit({
image: image,
prompt: prompt,
// Add unique identifier for tracking
metadata: { batchId: Date.now(), index }
});
return { success: true, result, index };
} catch (error) {
return { success: false, error, index };
}
})
);
return results;
};
// Usage
const images = [image1, image2, image3];
const results = await batchEdit(images, "Add vintage filter");
// Process results
results.forEach((result, index) => {
if (result.status === 'fulfilled' && result.value.success) {
console.log(`Image ${index} processed successfully`);
} else {
console.error(`Image ${index} failed:`, result.reason || result.value.error);
}
});
Batch Processing with Rate Limiting
class BatchProcessor {
constructor(client, options = {}) {
this.client = client;
this.concurrency = options.concurrency || 3;
this.delay = options.delay || 1000;
}
async processBatch(tasks) {
const results = [];
for (let i = 0; i < tasks.length; i += this.concurrency) {
const batch = tasks.slice(i, i + this.concurrency);
const batchResults = await Promise.allSettled(
batch.map(task => this.processTask(task))
);
results.push(...batchResults);
// Add delay between batches
if (i + this.concurrency < tasks.length) {
await new Promise(resolve => setTimeout(resolve, this.delay));
}
}
return results;
}
async processTask(task) {
return await this.client.edit(task);
}
}
// Usage
const processor = new BatchProcessor(client, {
concurrency: 2,
delay: 500
});
const tasks = images.map(image => ({
image,
prompt: "Enhance image quality"
}));
const results = await processor.processBatch(tasks);
Custom Models
Using Fine-tuned Models
Leverage custom models for specialized editing tasks:
// Use a custom fine-tuned model
const result = await client.edit({
image: inputImage,
prompt: "Apply brand-specific styling",
model: "custom-brand-model-v1",
parameters: {
strength: 0.8,
guidance_scale: 7.5,
custom_params: {
brand_style: "modern",
color_palette: "corporate"
}
}
});
Model Selection Strategy
class ModelSelector {
constructor() {
this.models = {
'portrait': 'qwen-portrait-v2',
'landscape': 'qwen-landscape-v2',
'product': 'qwen-product-v1',
'artistic': 'qwen-artistic-v3',
'custom-brand': 'custom-brand-model-v1'
};
}
selectModel(imageType, editType) {
// Logic to select appropriate model
if (editType === 'brand-styling') {
return this.models['custom-brand'];
}
return this.models[imageType] || 'qwen-general-v2';
}
}
const selector = new ModelSelector();
const model = selector.selectModel('portrait', 'enhancement');
const result = await client.edit({
image: inputImage,
prompt: "Enhance portrait lighting",
model: model
});
Advanced Editing Techniques
Multi-step Editing Pipeline
class EditingPipeline {
constructor(client) {
this.client = client;
this.steps = [];
}
addStep(prompt, options = {}) {
this.steps.push({ prompt, options });
return this;
}
async execute(inputImage) {
let currentImage = inputImage;
const results = [];
for (const [index, step] of this.steps.entries()) {
try {
const result = await this.client.edit({
image: currentImage,
prompt: step.prompt,
...step.options
});
currentImage = result.image;
results.push({
step: index + 1,
prompt: step.prompt,
success: true,
result
});
} catch (error) {
results.push({
step: index + 1,
prompt: step.prompt,
success: false,
error
});
break;
}
}
return {
finalImage: currentImage,
steps: results
};
}
}
// Usage
const pipeline = new EditingPipeline(client)
.addStep("Remove background", { strength: 0.9 })
.addStep("Add professional lighting", { strength: 0.7 })
.addStep("Enhance colors", { strength: 0.6 });
const result = await pipeline.execute(inputImage);
Conditional Editing
const conditionalEdit = async (image, conditions) => {
// Analyze image first
const analysis = await client.analyze({
image: image,
features: ['objects', 'colors', 'composition']
});
let editPrompt = "";
let editOptions = {};
// Apply conditions based on analysis
if (analysis.brightness < 0.3) {
editPrompt += "Brighten the image, ";
editOptions.strength = 0.8;
}
if (analysis.objects.includes('person')) {
editPrompt += "enhance portrait features, ";
editOptions.model = 'qwen-portrait-v2';
}
if (analysis.colors.saturation < 0.5) {
editPrompt += "increase color saturation";
}
// Remove trailing comma and space
editPrompt = editPrompt.replace(/, $/, '');
return await client.edit({
image: image,
prompt: editPrompt,
...editOptions
});
};
Performance Optimization
Image Preprocessing
class ImageOptimizer {
static async optimizeForEditing(image, options = {}) {
const {
maxWidth = 2048,
maxHeight = 2048,
quality = 0.9,
format = 'jpeg'
} = options;
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = () => {
// Calculate optimal dimensions
const ratio = Math.min(
maxWidth / img.width,
maxHeight / img.height,
1 // Don't upscale
);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
// Apply image smoothing
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(resolve, `image/${format}`, quality);
};
img.src = URL.createObjectURL(image);
});
}
}
// Usage
const optimizedImage = await ImageOptimizer.optimizeForEditing(originalImage, {
maxWidth: 1024,
quality: 0.85
});
const result = await client.edit({
image: optimizedImage,
prompt: "Professional photo enhancement"
});
Caching Strategy
class EditCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
}
generateKey(image, prompt, options = {}) {
// Create a hash of the input parameters
const params = JSON.stringify({ prompt, options });
return `${this.hashImage(image)}-${this.hashString(params)}`;
}
hashImage(image) {
// Simple hash based on image size and type
return `${image.size}-${image.type}-${image.lastModified || Date.now()}`;
}
hashString(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash.toString();
}
get(key) {
if (this.cache.has(key)) {
// Move to end (LRU)
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return null;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
// Remove oldest entry
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
// Usage with caching
const cache = new EditCache(50);
const cachedEdit = async (image, prompt, options = {}) => {
const key = cache.generateKey(image, prompt, options);
// Check cache first
const cached = cache.get(key);
if (cached) {
console.log('Cache hit!');
return cached;
}
// Process and cache result
const result = await client.edit({ image, prompt, ...options });
cache.set(key, result);
return result;
};
Enterprise Features
Webhook Integration
// Set up webhook for async processing
const setupWebhook = async () => {
await client.webhook.create({
url: 'https://your-app.com/webhooks/qwen',
events: ['edit.completed', 'edit.failed', 'batch.completed'],
secret: process.env.WEBHOOK_SECRET
});
};
// Handle webhook events
app.post('/webhooks/qwen', (req, res) => {
const signature = req.headers['x-qwen-signature'];
const payload = req.body;
// Verify webhook signature
if (!verifyWebhookSignature(payload, signature)) {
return res.status(401).send('Invalid signature');
}
switch (payload.event) {
case 'edit.completed':
handleEditCompleted(payload.data);
break;
case 'edit.failed':
handleEditFailed(payload.data);
break;
case 'batch.completed':
handleBatchCompleted(payload.data);
break;
}
res.status(200).send('OK');
});
Usage Analytics
class UsageTracker {
constructor(client) {
this.client = client;
this.metrics = {
totalEdits: 0,
successfulEdits: 0,
failedEdits: 0,
totalProcessingTime: 0,
averageProcessingTime: 0
};
}
async trackEdit(editFunction) {
const startTime = Date.now();
try {
const result = await editFunction();
const processingTime = Date.now() - startTime;
this.updateMetrics(true, processingTime);
return result;
} catch (error) {
this.updateMetrics(false, Date.now() - startTime);
throw error;
}
}
updateMetrics(success, processingTime) {
this.metrics.totalEdits++;
this.metrics.totalProcessingTime += processingTime;
if (success) {
this.metrics.successfulEdits++;
} else {
this.metrics.failedEdits++;
}
this.metrics.averageProcessingTime =
this.metrics.totalProcessingTime / this.metrics.totalEdits;
}
getMetrics() {
return {
...this.metrics,
successRate: this.metrics.successfulEdits / this.metrics.totalEdits,
failureRate: this.metrics.failedEdits / this.metrics.totalEdits
};
}
}
// Usage
const tracker = new UsageTracker(client);
const result = await tracker.trackEdit(async () => {
return await client.edit({
image: inputImage,
prompt: "Professional enhancement"
});
});
console.log('Usage metrics:', tracker.getMetrics());
Security Best Practices
API Key Management
// Secure API key rotation
class SecureClient {
constructor(options) {
this.primaryKey = options.primaryKey;
this.backupKey = options.backupKey;
this.keyRotationInterval = options.keyRotationInterval || 24 * 60 * 60 * 1000; // 24 hours
this.lastRotation = Date.now();
}
getCurrentKey() {
const now = Date.now();
if (now - this.lastRotation > this.keyRotationInterval) {
// Rotate keys
[this.primaryKey, this.backupKey] = [this.backupKey, this.primaryKey];
this.lastRotation = now;
}
return this.primaryKey;
}
async makeRequest(params) {
try {
return await this.client.edit({
...params,
apiKey: this.getCurrentKey()
});
} catch (error) {
if (error.status === 401) {
// Try backup key
return await this.client.edit({
...params,
apiKey: this.backupKey
});
}
throw error;
}
}
}
Input Validation
class SecureImageProcessor {
static validateImage(file) {
const maxSize = 10 * 1024 * 1024; // 10MB
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (file.size > maxSize) {
throw new Error('File size exceeds maximum limit');
}
if (!allowedTypes.includes(file.type)) {
throw new Error('Unsupported file type');
}
return true;
}
static sanitizePrompt(prompt) {
// Remove potentially harmful content
const sanitized = prompt
.replace(/<script[^>]*>.*?<\/script>/gi, '')
.replace(/javascript:/gi, '')
.replace(/on\w+\s*=/gi, '')
.trim();
if (sanitized.length > 500) {
throw new Error('Prompt too long');
}
return sanitized;
}
}
Monitoring and Logging
class AdvancedLogger {
constructor(options = {}) {
this.logLevel = options.logLevel || 'info';
this.enableMetrics = options.enableMetrics || false;
}
log(level, message, metadata = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
level,
message,
metadata,
requestId: metadata.requestId || this.generateRequestId()
};
if (this.shouldLog(level)) {
console.log(JSON.stringify(logEntry));
}
if (this.enableMetrics) {
this.sendToMetrics(logEntry);
}
}
shouldLog(level) {
const levels = ['debug', 'info', 'warn', 'error'];
return levels.indexOf(level) >= levels.indexOf(this.logLevel);
}
generateRequestId() {
return Math.random().toString(36).substring(2, 15);
}
sendToMetrics(logEntry) {
// Send to your metrics service
// Implementation depends on your monitoring stack
}
}
// Usage
const logger = new AdvancedLogger({ logLevel: 'info', enableMetrics: true });
const enhancedEdit = async (image, prompt, options = {}) => {
const requestId = logger.generateRequestId();
logger.log('info', 'Starting image edit', {
requestId,
imageSize: image.size,
prompt: prompt.substring(0, 100)
});
try {
const result = await client.edit({ image, prompt, ...options });
logger.log('info', 'Image edit completed', {
requestId,
success: true,
processingTime: result.processingTime
});
return result;
} catch (error) {
logger.log('error', 'Image edit failed', {
requestId,
error: error.message,
stack: error.stack
});
throw error;
}
};
These advanced features require appropriate API access levels and may incur additional costs. Contact support for enterprise feature access.