/** * 对比测试:Vision自动提取 vs 手写描述 * 生成Main_02平铺图,对比两者效果 */ require('dotenv').config(); const axios = require('axios'); const fs = require('fs'); const path = require('path'); const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ'; const API_BASE = 'https://api.wuyinkeji.com/api'; const OUTPUT_DIR = path.join(__dirname, 'output_comparison'); const MATERIAL_DIR = path.join(__dirname, '素材/素材/已有的素材'); // R2客户端 const r2Client = new S3Client({ region: 'auto', endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY, }, forcePathStyle: true, }); async function uploadToR2(filePath) { const fileName = `compare-${Date.now()}-${path.basename(filePath)}`; const fileBuffer = fs.readFileSync(filePath); await r2Client.send(new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME || 'ai-flow', Key: fileName, Body: fileBuffer, ContentType: filePath.endsWith('.png') ? 'image/png' : 'image/jpeg', })); return process.env.R2_PUBLIC_DOMAIN ? `${process.env.R2_PUBLIC_DOMAIN}/${fileName}` : `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com/${process.env.R2_BUCKET_NAME}/${fileName}`; } // ======================================== // 方案A: 手写的Golden Description (V2版) // ======================================== const MANUAL_GOLDEN_DESC = ` EXACT PRODUCT APPEARANCE (MUST MATCH PRECISELY): - Shape: 8-PETAL FLOWER/FAN shape, C-shaped opening (like Pac-Man), NOT a full circle - Color: ICE BLUE / Light aqua blue (#C5E8ED approximate) - Material: Glossy waterproof PU fabric with visible stitching lines between petals - Edge binding: TEAL/TURQUOISE color binding around the inner neck hole - Closure: White velcro strap on one petal end - Logo: "TOUCHDOG®" embroidered in matching blue thread on one petal (small, subtle) - Texture: Smooth, slightly shiny, NOT matte, NOT cotton fabric - Structure: Soft but structured, maintains petal shape, NOT floppy CRITICAL PROHIBITIONS: - ❌ NO printed patterns or colorful fabric designs - ❌ NO hard plastic transparent cones - ❌ NO fully circular/closed shapes - ❌ NO matte cotton or fleece textures - ❌ NO random brand logos or text `; // ======================================== // 方案B: Vision自动提取的Golden Description // ======================================== const VISION_GOLDEN_DESC = ` EXACT PRODUCT APPEARANCE (AUTO-EXTRACTED BY VISION): - Shape: 7-PETAL FLOWER/FAN shape, C-shaped opening - Color: PASTEL ICE BLUE (#C3E6E8) - Material: soft matte/satin synthetic fabric (likely water-resistant polyester/nylon) with smooth, padded/quilted panels texture - Edge binding: Mint Green (slightly more saturated than body) ribbed knit elastic fabric around inner neck hole - Closure: white velcro (hook and loop) on large rectangular strip covering the entire bottom-left terminal segment - Logo: "TOUCHDOG®" embroidered on centered on the middle-right segment UNIQUE FEATURES: - Scalloped outer edge resembling flower petals - Soft ribbed knit neckline for comfort - Radial stitching lines creating distinct padded zones - Large surface area velcro for adjustable sizing CRITICAL PROHIBITIONS: - ❌ NO printed patterns or colorful fabric designs - ❌ NO hard plastic transparent cones - ❌ NO fully circular/closed shapes (must have C-opening) - ❌ NO random brand logos or text `; // Main_02 平铺图 Prompt模板 function createMain02Prompt(goldenDesc) { return ` [PRODUCT FLAT LAY PHOTO - WHITE BACKGROUND] ${goldenDesc} SCENE REQUIREMENTS: - Product flat lay on PURE WHITE background (#FFFFFF) - Shot from directly above (bird's eye view / top-down angle) - Show the full C-shaped opening clearly (gap between velcro ends visible) - All petals/segments fully visible and spread out - Clean studio lighting, MINIMAL shadows - Product fills 70-80% of frame - Professional Amazon product photography style OUTPUT: High quality 1:1 aspect ratio product photo, 8K resolution `.trim(); } // 生图任务提交 async function submitImageTask(prompt, refImageUrl) { const payload = { key: API_KEY, prompt: prompt, img_url: refImageUrl, aspectRatio: '1:1', imageSize: '1K' }; const response = await axios.post(`${API_BASE}/img/nanoBanana-pro`, payload); const taskId = response.data.data?.id; if (!taskId) { throw new Error('No task ID returned'); } return taskId; } // 轮询图片结果 async function pollImageResult(taskId) { let attempts = 0; const maxAttempts = 60; while (attempts < maxAttempts) { const response = await axios.get(`${API_BASE}/img/drawDetail`, { params: { key: API_KEY, id: taskId } }); const data = response.data.data; if (data && data.status === 2 && data.image_url) { return data.image_url; } else if (data && data.status === 3) { throw new Error('Generation failed: ' + (data.fail_reason || 'Unknown')); } process.stdout.write('.'); await new Promise(r => setTimeout(r, 2000)); attempts++; } throw new Error('Timeout waiting for image'); } // 生成单张图 async function generateImage(name, prompt, refUrl) { console.log(`\n🎨 生成: ${name}`); console.log(' 提交任务...'); const taskId = await submitImageTask(prompt, refUrl); console.log(` Task ID: ${taskId}`); const imageUrl = await pollImageResult(taskId); console.log('\n ✓ 生成成功'); // 下载保存 const imgRes = await axios.get(imageUrl, { responseType: 'arraybuffer' }); const outputPath = path.join(OUTPUT_DIR, `${name}.jpg`); fs.writeFileSync(outputPath, imgRes.data); console.log(` ✓ 保存: ${outputPath}`); return outputPath; } async function main() { if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true }); console.log('='.repeat(60)); console.log('🔬 Vision提取 vs 手写描述 对比测试'); console.log('='.repeat(60)); // 上传参考图 console.log('\n📤 上传参考图...'); const flatImgPath = path.join(MATERIAL_DIR, 'IMG_5683.png'); const refUrl = await uploadToR2(flatImgPath); console.log(' 参考图URL:', refUrl); // 生成两个版本的Main_02 const results = []; // 版本A: 手写描述 console.log('\n' + '='.repeat(60)); console.log('📝 版本A: 手写Golden Description'); console.log('='.repeat(60)); const promptA = createMain02Prompt(MANUAL_GOLDEN_DESC); console.log('Prompt预览:\n', promptA.substring(0, 500) + '...'); try { const pathA = await generateImage('Main_02_Manual', promptA, refUrl); results.push({ name: 'Manual', path: pathA, success: true }); } catch (e) { console.error('版本A失败:', e.message); results.push({ name: 'Manual', success: false, error: e.message }); } // 版本B: Vision提取 console.log('\n' + '='.repeat(60)); console.log('🤖 版本B: Vision自动提取Golden Description'); console.log('='.repeat(60)); const promptB = createMain02Prompt(VISION_GOLDEN_DESC); console.log('Prompt预览:\n', promptB.substring(0, 500) + '...'); try { const pathB = await generateImage('Main_02_Vision', promptB, refUrl); results.push({ name: 'Vision', path: pathB, success: true }); } catch (e) { console.error('版本B失败:', e.message); results.push({ name: 'Vision', success: false, error: e.message }); } // 总结 console.log('\n' + '='.repeat(60)); console.log('📊 对比结果'); console.log('='.repeat(60)); console.log(`输出目录: ${OUTPUT_DIR}`); console.log('\n生成结果:'); results.forEach(r => { if (r.success) { console.log(` ✅ ${r.name}: ${r.path}`); } else { console.log(` ❌ ${r.name}: ${r.error}`); } }); console.log('\n💡 请手动对比两张图片,评估Vision自动提取的效果是否达标'); } main().catch(console.error);