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.