Upload latest code and optimized prompts (v6)
This commit is contained in:
395
poc-workflow-v2.js
Normal file
395
poc-workflow-v2.js
Normal file
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* POC Workflow V2: 强化产品一致性
|
||||
* 核心改进:
|
||||
* 1. 极致详细的产品描述 (Golden Description)
|
||||
* 2. 移除AI生成logo(后期处理)
|
||||
* 3. 分级策略:A级严格复制,B级可控创意
|
||||
* 4. 对比图特殊处理
|
||||
*/
|
||||
|
||||
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_poc_v2');
|
||||
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 = `poc-v2-${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',
|
||||
}));
|
||||
|
||||
const publicUrl = 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}`;
|
||||
|
||||
return publicUrl;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 🔥 核心改进1: 黄金产品描述 (Golden Product Description)
|
||||
// ============================================================
|
||||
const GOLDEN_PRODUCT_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
|
||||
`;
|
||||
|
||||
// ============================================================
|
||||
// 🔥 核心改进2: 分级Prompt策略
|
||||
// ============================================================
|
||||
|
||||
// A级:严格产品复制 (必须和参考图几乎一致)
|
||||
function createPromptA_Strict(sceneDesc) {
|
||||
return `
|
||||
[PRODUCT REPRODUCTION - HIGHEST FIDELITY]
|
||||
${GOLDEN_PRODUCT_DESC}
|
||||
|
||||
SCENE: ${sceneDesc}
|
||||
|
||||
INSTRUCTION: Reproduce the EXACT product from reference image.
|
||||
The product shape, color, and material must be IDENTICAL to reference.
|
||||
Only change the background/scene as described.
|
||||
DO NOT add any brand logo overlays or text graphics.
|
||||
`.trim();
|
||||
}
|
||||
|
||||
// B级:可控创意 (产品保持一致,场景可发挥)
|
||||
function createPromptB_Controlled(sceneDesc, extraInstructions = '') {
|
||||
return `
|
||||
[CONTROLLED CREATIVE - PRODUCT CONSISTENCY REQUIRED]
|
||||
${GOLDEN_PRODUCT_DESC}
|
||||
|
||||
SCENE: ${sceneDesc}
|
||||
|
||||
${extraInstructions}
|
||||
|
||||
INSTRUCTION: Keep product appearance EXACTLY as described above.
|
||||
Creative freedom allowed ONLY for background, lighting, and composition.
|
||||
DO NOT modify product shape, color, or material.
|
||||
DO NOT add any brand logo overlays.
|
||||
`.trim();
|
||||
}
|
||||
|
||||
// C级:对比图专用 (左边严格,右边自由)
|
||||
function createPromptC_Comparison() {
|
||||
return `
|
||||
[COMPARISON IMAGE - SPLIT SCREEN]
|
||||
|
||||
LEFT SIDE (OUR PRODUCT - STRICT):
|
||||
${GOLDEN_PRODUCT_DESC}
|
||||
- Happy, comfortable cat/dog wearing the ICE BLUE 8-PETAL soft cone
|
||||
- Bright, colorful, warm lighting
|
||||
- GREEN CHECKMARK overlay
|
||||
|
||||
RIGHT SIDE (GENERIC COMPETITOR - FLEXIBLE):
|
||||
- Generic transparent HARD PLASTIC cone (the traditional "lamp shade" type)
|
||||
- Sad, uncomfortable looking cat/dog
|
||||
- Grayscale / desaturated colors
|
||||
- RED X-MARK overlay
|
||||
|
||||
LAYOUT: Side by side, clear visual contrast
|
||||
TEXT HEADER: "Soft & Comfortable" vs "Hard & Uncomfortable"
|
||||
|
||||
CRITICAL: Left side product must be ICE BLUE 8-PETAL FLOWER shape, NOT any other design!
|
||||
`.trim();
|
||||
}
|
||||
|
||||
// 生图任务提交
|
||||
async function submitImageTask(prompt, refImageUrl, aspectRatio = '1:1') {
|
||||
try {
|
||||
const payload = {
|
||||
key: API_KEY,
|
||||
prompt: prompt,
|
||||
img_url: refImageUrl, // 强参考图
|
||||
aspectRatio: aspectRatio,
|
||||
imageSize: '1K'
|
||||
};
|
||||
|
||||
console.log(` 提交任务...`);
|
||||
|
||||
const response = await axios.post(`${API_BASE}/img/nanoBanana-pro`, payload);
|
||||
const taskId = response.data.data?.id;
|
||||
|
||||
if (!taskId) {
|
||||
console.error('Submit response:', response.data);
|
||||
throw new Error('No task ID returned');
|
||||
}
|
||||
|
||||
return taskId;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Submit failed:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 轮询图片结果
|
||||
async function pollImageResult(taskId) {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 60;
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
try {
|
||||
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++;
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes('Generation failed')) throw error;
|
||||
console.error('Poll error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
throw new Error('Timeout waiting for image');
|
||||
}
|
||||
|
||||
// 生成单张图(含重试)
|
||||
async function generateSingleImage(item, refUrls) {
|
||||
console.log(`\n🎨 [${item.level}级] ${item.id}: ${item.name}`);
|
||||
|
||||
let retryCount = 0;
|
||||
const maxRetries = 2;
|
||||
|
||||
// 根据类型选择最佳参考图
|
||||
let refUrl = refUrls.flat; // 默认用平铺图
|
||||
if (item.useWornRef) {
|
||||
refUrl = refUrls.worn;
|
||||
}
|
||||
|
||||
while (retryCount <= maxRetries) {
|
||||
try {
|
||||
if (retryCount > 0) console.log(` 重试第 ${retryCount} 次...`);
|
||||
|
||||
const taskId = await submitImageTask(item.prompt, refUrl, item.aspectRatio);
|
||||
console.log(` Task ID: ${taskId}`);
|
||||
|
||||
const imageUrl = await pollImageResult(taskId);
|
||||
console.log('\n ✓ 生成成功');
|
||||
|
||||
// 下载保存
|
||||
const imgRes = await axios.get(imageUrl, { responseType: 'arraybuffer' });
|
||||
fs.writeFileSync(path.join(OUTPUT_DIR, `${item.id}.jpg`), imgRes.data);
|
||||
console.log(' ✓ 保存本地');
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`\n ✗ 失败: ${error.message}`);
|
||||
retryCount++;
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
}
|
||||
}
|
||||
console.error(` ❌ ${item.id} 最终失败`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 主流程
|
||||
// ============================================================
|
||||
async function main() {
|
||||
if (!fs.existsSync(OUTPUT_DIR)) fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
|
||||
// 1. 上传核心参考图
|
||||
console.log('📤 上传核心参考图...');
|
||||
|
||||
const flatImgPath = path.join(MATERIAL_DIR, 'IMG_5683.png'); // 平铺图(最清晰产品细节)
|
||||
const wornImgPath = path.join(MATERIAL_DIR, 'IMG_6514.JPG'); // 佩戴图
|
||||
|
||||
const refUrls = {
|
||||
flat: await uploadToR2(flatImgPath),
|
||||
worn: await uploadToR2(wornImgPath)
|
||||
};
|
||||
|
||||
console.log(' 平铺图:', refUrls.flat);
|
||||
console.log(' 佩戴图:', refUrls.worn);
|
||||
|
||||
// 2. 定义优化后的图片任务
|
||||
const tasks = [
|
||||
// ========== A级:严格复制 ==========
|
||||
{
|
||||
id: 'Main_01_Hero',
|
||||
name: '场景首图',
|
||||
level: 'A',
|
||||
aspectRatio: '1:1',
|
||||
useWornRef: true,
|
||||
prompt: createPromptA_Strict(`
|
||||
Cute white fluffy cat wearing the product.
|
||||
Cone opens OUTWARD around face (like flower petals spreading out).
|
||||
Cat looks comfortable and relaxed.
|
||||
Warm, cozy home interior background (soft focus).
|
||||
Professional Amazon product photography, 8K quality.
|
||||
`)
|
||||
},
|
||||
{
|
||||
id: 'Main_02_Flat',
|
||||
name: '平铺白底图',
|
||||
level: 'A',
|
||||
aspectRatio: '1:1',
|
||||
useWornRef: false,
|
||||
prompt: createPromptA_Strict(`
|
||||
Product flat lay on PURE WHITE background.
|
||||
Shot from directly above (bird's eye view).
|
||||
Show the 8-petal flower/fan shape clearly.
|
||||
C-shaped opening (gap between velcro ends) visible.
|
||||
Clean studio lighting, no shadows.
|
||||
Product photography style.
|
||||
`)
|
||||
},
|
||||
|
||||
// ========== B级:可控创意 ==========
|
||||
{
|
||||
id: 'Main_03_Function',
|
||||
name: '功能演示',
|
||||
level: 'B',
|
||||
aspectRatio: '1:1',
|
||||
useWornRef: false,
|
||||
prompt: createPromptB_Controlled(`
|
||||
Close-up of hands adjusting the white velcro strap.
|
||||
Show the velcro closure mechanism clearly.
|
||||
Demonstrate "adjustable fit" feature.
|
||||
Clean, bright lighting.
|
||||
`)
|
||||
},
|
||||
{
|
||||
id: 'Main_04_Scenarios',
|
||||
name: '多场景使用',
|
||||
level: 'B',
|
||||
aspectRatio: '1:1',
|
||||
useWornRef: true,
|
||||
prompt: createPromptB_Controlled(`
|
||||
2x2 grid showing different usage scenarios:
|
||||
- Cat eating from bowl while wearing cone (cone allows eating)
|
||||
- Cat drinking water comfortably
|
||||
- Cat sleeping peacefully
|
||||
- Cat walking around home
|
||||
All showing the ICE BLUE 8-PETAL cone product.
|
||||
`)
|
||||
},
|
||||
{
|
||||
id: 'APlus_01_Banner',
|
||||
name: 'A+横幅',
|
||||
level: 'B',
|
||||
aspectRatio: '3:2',
|
||||
useWornRef: true,
|
||||
prompt: createPromptB_Controlled(`
|
||||
Wide banner shot for Amazon A+ content.
|
||||
Beautiful modern living room scene.
|
||||
White cat wearing the ICE BLUE 8-petal cone walking on soft rug.
|
||||
Left side has empty space for text overlay.
|
||||
Warm, inviting home atmosphere.
|
||||
Professional lifestyle photography.
|
||||
`)
|
||||
},
|
||||
{
|
||||
id: 'APlus_03_Detail',
|
||||
name: '材质细节',
|
||||
level: 'B',
|
||||
aspectRatio: '3:2',
|
||||
useWornRef: false,
|
||||
prompt: createPromptB_Controlled(`
|
||||
Extreme close-up macro photography.
|
||||
Focus on the ICE BLUE glossy PU material texture.
|
||||
Water droplets beading on the waterproof surface.
|
||||
Show the neat stitching between petals.
|
||||
Show the TEAL edge binding around neck hole.
|
||||
High-end product detail photography.
|
||||
`)
|
||||
},
|
||||
{
|
||||
id: 'APlus_04_Waterproof',
|
||||
name: '防水演示',
|
||||
level: 'B',
|
||||
aspectRatio: '3:2',
|
||||
useWornRef: false,
|
||||
prompt: createPromptB_Controlled(`
|
||||
Action shot demonstrating waterproof feature.
|
||||
Water being poured onto the ICE BLUE 8-petal cone.
|
||||
Water droplets rolling off the glossy PU surface.
|
||||
Dynamic splash photography style.
|
||||
Bright lighting, white background.
|
||||
`)
|
||||
},
|
||||
|
||||
// ========== C级:对比图 ==========
|
||||
{
|
||||
id: 'APlus_02_Comparison',
|
||||
name: '对比图',
|
||||
level: 'C',
|
||||
aspectRatio: '3:2',
|
||||
useWornRef: true,
|
||||
prompt: createPromptC_Comparison()
|
||||
},
|
||||
];
|
||||
|
||||
// 3. 执行生成
|
||||
console.log('\n🚀 开始V2优化版生成...\n');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
let successCount = 0;
|
||||
for (const task of tasks) {
|
||||
const success = await generateSingleImage(task, refUrls);
|
||||
if (success) successCount++;
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log(`✅ 完成! 成功 ${successCount}/${tasks.length} 张`);
|
||||
console.log(`📁 输出目录: ${OUTPUT_DIR}`);
|
||||
|
||||
// 4. 说明跳过的图
|
||||
console.log('\n⚠️ 以下图片建议后期处理,不适合纯AI生成:');
|
||||
console.log(' - Main_05_Size (尺寸图) → 用设计软件+产品抠图');
|
||||
console.log(' - Main_06_Brand (品牌图) → 用设计软件+官方logo');
|
||||
console.log(' - APlus_05_Storage (收纳图) → 需要真实折叠产品素材');
|
||||
console.log(' - APlus_06_Guide (选购指南) → 用设计软件模板');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user