Files
amz-pic-flow/test-vision-vs-manual.js

241 lines
7.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 对比测试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);