UNPKG

@bramato/openrouter-mock-generator

Version:

AI-powered mock data generator using OpenRouter API with JSON mode support

380 lines 15 kB
"use strict"; /** * Microservizio per analizzare e determinare il tipo di elaborazione per le immagini */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ImageProcessingAnalyzer = void 0; class ImageProcessingAnalyzer { /** * Analizza e crea un piano di elaborazione ottimizzato */ static createProcessingPlan(images, descriptions) { // Raggruppa immagini per similarità di contenuto const similarityGroups = this.groupBySimilarity(images, descriptions); // Crea gruppi di elaborazione ottimizzati const processingGroups = similarityGroups.map(group => this.optimizeGroup(group, descriptions)); return this.calculateOptimizationResult(processingGroups); } /** * Raggruppa immagini per similarità di contenuto */ static groupBySimilarity(images, descriptions) { const groups = []; const processed = new Set(); images.forEach(image => { if (processed.has(image.path)) return; const currentGroup = [image]; processed.add(image.path); const currentDesc = descriptions.get(image.path); if (!currentDesc) return; // Trova immagini simili images.forEach(otherImage => { if (processed.has(otherImage.path)) return; const otherDesc = descriptions.get(otherImage.path); if (!otherDesc) return; const similarity = this.calculateDescriptionSimilarity(currentDesc, otherDesc); const contextSimilarity = this.calculateContextSimilarity(image, otherImage); if (similarity >= this.SIMILARITY_THRESHOLD || contextSimilarity >= this.SIMILARITY_THRESHOLD) { currentGroup.push(otherImage); processed.add(otherImage.path); } }); groups.push(currentGroup); }); return groups; } /** * Calcola similarità tra descrizioni */ static calculateDescriptionSimilarity(desc1, desc2) { // Similarità basata su categoria const categorySimilarity = desc1.category === desc2.category ? 0.4 : 0; // Similarità basata su parole chiave nel prompt const words1 = new Set(desc1.prompt.toLowerCase().split(/\s+/)); const words2 = new Set(desc2.prompt.toLowerCase().split(/\s+/)); const intersection = new Set([...words1].filter(x => words2.has(x))); const union = new Set([...words1, ...words2]); const wordSimilarity = (intersection.size / union.size) * 0.4; // Similarità di stile const styleSimilarity = desc1.style === desc2.style ? 0.2 : 0; return categorySimilarity + wordSimilarity + styleSimilarity; } /** * Calcola similarità tra contesti */ static calculateContextSimilarity(img1, img2) { const context1 = img1.context; const context2 = img2.context; if (!context1 || !context2) return 0; let similarity = 0; let comparisons = 0; // Confronta campi chiave const keyFields = ['category', 'type', 'brand', 'location', 'name']; keyFields.forEach(field => { if (context1[field] && context2[field]) { comparisons++; if (context1[field] === context2[field]) { similarity += 1; } else if (typeof context1[field] === 'string' && typeof context2[field] === 'string') { // Calcola similarità tra stringhe const stringSim = this.calculateStringSimilarity(context1[field], context2[field]); similarity += stringSim; } } }); return comparisons > 0 ? similarity / comparisons : 0; } /** * Calcola similarità tra stringhe */ static calculateStringSimilarity(str1, str2) { const longer = str1.length > str2.length ? str1 : str2; const shorter = str1.length > str2.length ? str2 : str1; if (longer.length === 0) return 1.0; const distance = this.levenshteinDistance(longer, shorter); return (longer.length - distance) / longer.length; } /** * Calcola distanza di Levenshtein */ static levenshteinDistance(str1, str2) { const matrix = []; for (let i = 0; i <= str2.length; i++) { matrix[i] = [i]; } for (let j = 0; j <= str1.length; j++) { matrix[0][j] = j; } for (let i = 1; i <= str2.length; i++) { for (let j = 1; j <= str1.length; j++) { if (str2.charAt(i - 1) === str1.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution matrix[i][j - 1] + 1, // insertion matrix[i - 1][j] + 1 // deletion ); } } } return matrix[str2.length][str1.length]; } /** * Ottimizza un gruppo di immagini simili */ static optimizeGroup(images, descriptions) { if (images.length === 1) { const image = images[0]; const description = descriptions.get(image.path); return { masterImageId: image.path, description, targetDimensions: image.dimensions, variants: [ { imageId: image.path, processingType: 'generate', targetDimensions: image.dimensions, description, priority: 1, estimatedCost: 1, }, ], totalCost: 1, }; } // Trova l'immagine "master" (la più grande o con maggiore confidence) const masterImage = this.selectMasterImage(images, descriptions); const masterDescription = descriptions.get(masterImage.path); // Crea piani per le varianti const variants = []; let totalCost = 1; // Costo per generare l'immagine master // Piano per l'immagine master variants.push({ imageId: masterImage.path, processingType: 'generate', targetDimensions: masterImage.dimensions, description: masterDescription, priority: 1, estimatedCost: 1, }); // Piani per le varianti images.forEach(image => { if (image.path === masterImage.path) return; const plan = this.createVariantPlan(image, masterImage, descriptions); variants.push(plan); totalCost += plan.estimatedCost; }); return { masterImageId: masterImage.path, description: masterDescription, targetDimensions: masterImage.dimensions, variants, totalCost, }; } /** * Seleziona l'immagine master da un gruppo */ static selectMasterImage(images, descriptions) { return images.reduce((best, current) => { const bestDesc = descriptions.get(best.path); const currentDesc = descriptions.get(current.path); // Priorità: confidence maggiore, poi dimensioni maggiori const bestScore = (bestDesc?.confidence || 0) * 100 + (best.dimensions.width * best.dimensions.height) / 10000; const currentScore = (currentDesc?.confidence || 0) * 100 + (current.dimensions.width * current.dimensions.height) / 10000; return currentScore > bestScore ? current : best; }); } /** * Crea piano per una variante basata sull'immagine master */ static createVariantPlan(targetImage, masterImage, descriptions) { const targetDesc = descriptions.get(targetImage.path); const masterDims = masterImage.dimensions; const targetDims = targetImage.dimensions; // Calcola rapporti const widthRatio = targetDims.width / masterDims.width; const heightRatio = targetDims.height / masterDims.height; const aspectRatioMatch = Math.abs(widthRatio - heightRatio) < 0.1; // Determina tipo di elaborazione let processingType = 'generate'; let cropRegion; let estimatedCost = 1; // Se le dimensioni sono identiche, riusa direttamente if (targetDims.width === masterDims.width && targetDims.height === masterDims.height) { processingType = 'reuse'; estimatedCost = 0.1; } // Se il rapporto dimensionale è ok, ridimensiona else if (aspectRatioMatch && widthRatio >= this.MIN_RESIZE_RATIO && widthRatio <= this.MAX_RESIZE_RATIO) { processingType = 'resize'; estimatedCost = 0.2; } // Se può essere ritagliata efficacemente else if (this.canCropEfficiently(masterDims, targetDims)) { processingType = 'crop'; cropRegion = this.calculateOptimalCrop(masterDims, targetDims); estimatedCost = 0.3; } // Altrimenti genera nuova immagine else { processingType = 'generate'; estimatedCost = 1; } return { imageId: targetImage.path, processingType, sourceImageId: processingType !== 'generate' ? masterImage.path : undefined, targetDimensions: targetDims, cropRegion, description: targetDesc, priority: this.calculatePriority(targetImage, targetDesc), estimatedCost, }; } /** * Verifica se un'immagine può essere ritagliata efficacemente */ static canCropEfficiently(sourceDims, targetDims) { const sourceArea = sourceDims.width * sourceDims.height; const targetArea = targetDims.width * targetDims.height; const efficiency = targetArea / sourceArea; return (targetDims.width <= sourceDims.width && targetDims.height <= sourceDims.height && efficiency >= this.CROP_EFFICIENCY_THRESHOLD); } /** * Calcola ritaglio ottimale */ static calculateOptimalCrop(sourceDims, targetDims) { // Centro il ritaglio const x = Math.max(0, (sourceDims.width - targetDims.width) / 2); const y = Math.max(0, (sourceDims.height - targetDims.height) / 2); return { x: Math.floor(x), y: Math.floor(y), width: targetDims.width, height: targetDims.height, }; } /** * Calcola priorità di elaborazione */ static calculatePriority(image, description) { let priority = description.confidence * 10; // Aumenta priorità per immagini importanti const fieldName = image.fieldName.toLowerCase(); if (fieldName.includes('banner') || fieldName.includes('hero')) { priority += 5; } else if (fieldName.includes('thumb')) { priority -= 2; } // Dimensioni maggiori = priorità maggiore const area = image.dimensions.width * image.dimensions.height; priority += Math.log10(area / 10000); return Math.max(1, Math.min(10, Math.round(priority))); } /** * Calcola risultato di ottimizzazione */ static calculateOptimizationResult(groups) { let totalImages = 0; let generatedImages = 0; let resizedImages = 0; let croppedImages = 0; let reusedImages = 0; let totalCostWithoutOptimization = 0; let totalCostWithOptimization = 0; groups.forEach(group => { totalImages += group.variants.length; totalCostWithoutOptimization += group.variants.length; // Senza ottimizzazione, ogni immagine costa 1 totalCostWithOptimization += group.totalCost; group.variants.forEach(variant => { switch (variant.processingType) { case 'generate': generatedImages++; break; case 'resize': resizedImages++; break; case 'crop': croppedImages++; break; case 'reuse': reusedImages++; break; } }); }); const estimatedSavings = ((totalCostWithoutOptimization - totalCostWithOptimization) / totalCostWithoutOptimization) * 100; return { groups, totalImages, generatedImages, resizedImages, croppedImages, reusedImages, estimatedSavings, }; } /** * Ordina gruppi per priorità di elaborazione */ static sortGroupsByPriority(groups) { return groups.sort((a, b) => { const avgPriorityA = a.variants.reduce((sum, v) => sum + v.priority, 0) / a.variants.length; const avgPriorityB = b.variants.reduce((sum, v) => sum + v.priority, 0) / b.variants.length; return avgPriorityB - avgPriorityA; }); } /** * Filtra piani per tipo di elaborazione */ static filterPlansByType(groups, type) { return groups.flatMap(group => group.variants.filter(variant => variant.processingType === type)); } /** * Statistiche di ottimizzazione */ static getOptimizationStats(result) { const breakdown = { 'Immagini totali': result.totalImages, 'Nuove generazioni': result.generatedImages, Ridimensionamenti: result.resizedImages, Ritagli: result.croppedImages, Riutilizzi: result.reusedImages, }; const efficiencyScore = Math.round(((result.resizedImages + result.croppedImages + result.reusedImages) / result.totalImages) * 100); const summary = `Ottimizzazione: ${Math.round(result.estimatedSavings)}% risparmio, ` + `${result.generatedImages}/${result.totalImages} generazioni necessarie`; return { summary, breakdown, efficiencyScore, }; } } exports.ImageProcessingAnalyzer = ImageProcessingAnalyzer; ImageProcessingAnalyzer.SIMILARITY_THRESHOLD = 0.8; ImageProcessingAnalyzer.MIN_RESIZE_RATIO = 0.5; ImageProcessingAnalyzer.MAX_RESIZE_RATIO = 2.0; ImageProcessingAnalyzer.CROP_EFFICIENCY_THRESHOLD = 0.6; //# sourceMappingURL=image-processing-analyzer.js.map