Files
amz-pic-flow/poc-workflow-v6.js

877 lines
33 KiB
JavaScript
Raw 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.

/**
* POC Workflow V6: 优化Prompt控制策略
*
* 核心改进:
* 1. 修复素材选择(使用猫咪图而非狗狗图)
* 2. 优化Prompt - 更精准地指示AI保持主体不变
* 3. 分类处理不同复杂度的图片
*/
require('dotenv').config();
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const imageProcessor = require('./lib/image-processor');
// ============================================================
// 配置
// ============================================================
const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ';
const API_BASE = 'https://api.wuyinkeji.com/api';
// 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(buffer, filename) {
const fileName = `v6-${Date.now()}-${filename}`;
await r2Client.send(new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME || 'ai-flow',
Key: fileName,
Body: buffer,
ContentType: filename.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}`;
}
async function submitImageTask(prompt, refImageUrl = null, aspectRatio = '1:1') {
const payload = {
key: API_KEY,
prompt: prompt,
aspectRatio: aspectRatio,
imageSize: '1K'
};
if (refImageUrl) {
payload.img_url = refImageUrl;
}
const response = await axios.post(`${API_BASE}/img/nanoBanana-pro`, payload, { timeout: 30000 });
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 = 90;
while (attempts < maxAttempts) {
try {
const response = await axios.get(`${API_BASE}/img/drawDetail`, {
params: { key: API_KEY, id: taskId },
timeout: 10000
});
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;
process.stdout.write('x');
await new Promise(r => setTimeout(r, 3000));
attempts++;
}
}
throw new Error('Timeout');
}
async function downloadImage(url) {
const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 30000 });
return Buffer.from(response.data);
}
async function generateAIImage(prompt, refImageUrl = null, aspectRatio = '1:1') {
console.log(' 提交AI任务...');
const taskId = await submitImageTask(prompt, refImageUrl, aspectRatio);
console.log(` Task ID: ${taskId}`);
const imageUrl = await pollImageResult(taskId);
console.log('\n ✓ AI生成完成');
return await downloadImage(imageUrl);
}
// ============================================================
// Prompt模板 - 核心优化
// ============================================================
/**
* 构建精准的编辑prompt
* 核心策略明确告诉AI这是"编辑"任务,精确描述要保留的元素
*/
function buildEditPrompt(config) {
const {
subjectDescription, // 主体描述(产品+宠物)
preserveElements, // 必须保留的元素
editTasks, // 编辑任务
layoutDescription, // 排版描述
styleGuide, // 风格指南
aspectRatio // 比例
} = config;
return `
[IMAGE EDITING TASK - NOT GENERATION]
REFERENCE IMAGE ANALYSIS:
The reference image shows: ${subjectDescription}
CRITICAL PRESERVATION REQUIREMENTS (DO NOT MODIFY):
${preserveElements.map((e, i) => `${i + 1}. ${e}`).join('\n')}
EDITING TASKS (ONLY THESE CHANGES ARE ALLOWED):
${editTasks.map((t, i) => `${i + 1}. ${t}`).join('\n')}
LAYOUT SPECIFICATION:
${layoutDescription}
STYLE GUIDE:
${styleGuide}
OUTPUT: ${aspectRatio} ratio, professional Amazon listing quality
FINAL CHECK: Before output, verify that the pet and product in the result match the reference image EXACTLY in:
- Species, breed, fur color and pattern
- Product color (#B5E5E8 ice blue), shape (flower/petal cone), brand text "TOUCHDOG"
- Pose and positioning relative to original
`.trim();
}
// ============================================================
// 12张图的生成逻辑
// ============================================================
/**
* Main_01: 场景首图+卖点
*/
async function generateMain01(materials, config, outputDir) {
console.log('\n🎨 [Main_01] 场景首图+卖点');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'cat-worn.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat (white/cream fur with light brown markings, blue eyes) wearing an ice-blue soft recovery cone collar (brand: TOUCHDOG). The cat is in a home environment.`,
preserveElements: [
`The cat MUST remain a Ragdoll with blue eyes, white/cream fur, brown ear markings - EXACTLY as shown`,
`The cone collar MUST remain ice-blue (#B5E5E8), flower/petal shaped, with "TOUCHDOG" branding`,
`The cat's pose, expression, and the way the collar sits on the cat`,
`The product's texture, stitching pattern, and velcro closure detail`
],
editTasks: [
`Enhance background to warm, cozy home interior (fireplace, blanket, wooden furniture)`,
`Add soft warm lighting with gentle shadows`,
`Add a curved blue banner (#4A7C9B) across middle area with text: "DESIGNED FOR COMFORTABLE RECOVERY"`,
`Add 3 white feature boxes at bottom with icons and text:
- Egg icon + "LIGHTER THAN AN EGG"
- Water drop icon + "WATERPROOF & EASY WIPE"
- Cloud icon + "BREATHABLE COTTON LINING"`,
`Add subtle paw print watermarks in light blue on the banner area`
],
layoutDescription: `
- Top 60%: Cat wearing product in enhanced home scene
- Middle: Curved banner with main tagline
- Bottom 25%: Three feature callout boxes in a row`,
styleGuide: `Professional Amazon main image style. Warm color palette. Text should be crisp and readable. The cat and product should be the clear focal point.`,
aspectRatio: '1:1 square'
});
const result = await generateAIImage(prompt, wornUrl, '1:1');
await imageProcessor.saveImage(result, path.join(outputDir, 'Main_01.jpg'));
// 保存prompt供调试
fs.writeFileSync(path.join(outputDir, 'Main_01_prompt.txt'), prompt);
return true;
}
/**
* Main_03: 功能调节展示 (参考效果最好的那张)
*/
async function generateMain03(materials, config, outputDir) {
console.log('\n🎨 [Main_03] 功能调节展示');
// 使用平铺图作为主参考
const flatBuffer = fs.readFileSync(materials.flatImage);
const flatUrl = await uploadToR2(flatBuffer, 'flat.png');
const prompt = buildEditPrompt({
subjectDescription: `An ice-blue soft pet recovery cone collar (TOUCHDOG brand) shown in flat lay position. The product has a flower/petal shape with 8 segments, velcro closure, and green inner rim.`,
preserveElements: [
`The product MUST remain EXACTLY as shown: ice-blue color (#B5E5E8), flower shape, TOUCHDOG branding`,
`The velcro strap detail on the left side`,
`The green/teal inner rim around the neck hole`,
`The 8-petal segmented design with visible stitching`
],
editTasks: [
`Place the product on a soft blue background (#6B9AC4) that complements the product color`,
`Add title text at top-left: "ADJUSTABLE STRAP FOR A SECURE FIT" in white, bold`,
`Add 2 circular detail callout images on the right side:
- Top circle: Close-up of the velcro strap, caption "SECURE THE ADJUSTABLE STRAP"
- Bottom circle: Product being worn by a pet showing fit, caption "ADJUST FOR A SNUG FIT"`,
`Add decorative white paw prints scattered in the background`,
`Add thin white circular borders around the detail callouts`
],
layoutDescription: `
- Left side (60%): Main product image, slightly angled
- Top-left corner: Title text
- Right side (40%): Two stacked circular detail images with captions
- Scattered paw prints as decoration`,
styleGuide: `Clean infographic style. Blue color scheme matching the product. Professional Amazon A+ content quality. High contrast text for readability.`,
aspectRatio: '1:1 square'
});
const result = await generateAIImage(prompt, flatUrl, '1:1');
await imageProcessor.saveImage(result, path.join(outputDir, 'Main_03.jpg'));
fs.writeFileSync(path.join(outputDir, 'Main_03_prompt.txt'), prompt);
return true;
}
/**
* Main_04: 多场景使用 - 关键优化
* 这是最难的图需要在4个场景中保持同一产品和宠物
*/
async function generateMain04(materials, config, outputDir) {
console.log('\n🎨 [Main_04] 多场景使用 (4宫格)');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'cat-worn-grid.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat (white/cream long fur, light brown markings on ears and face, distinctive blue eyes, pink nose) wearing an ice-blue TOUCHDOG soft recovery cone collar.`,
preserveElements: [
`ALL 4 SCENES MUST SHOW THE EXACT SAME CAT: Ragdoll breed, blue eyes, white/cream fur, brown ear markings`,
`ALL 4 SCENES MUST SHOW THE EXACT SAME PRODUCT: Ice-blue (#B5E5E8) flower-shaped soft cone, TOUCHDOG brand`,
`The cat's fur texture and coloring must be consistent across all 4 images`,
`The product's color, shape, and branding must be identical in all 4 scenes`
],
editTasks: [
`Create a 2x2 grid layout with 4 scenes, each in a rounded rectangle frame`,
`TOP-LEFT scene: Cat standing alert, wearing the cone - Caption: "• HYGIENIC & EASY TO CLEAN"`,
`TOP-RIGHT scene: Cat eating/drinking from a bowl while wearing cone - Caption: "• UNRESTRICTED EATING/DRINKING"`,
`BOTTOM-LEFT scene: Cat stretching or playing while wearing cone - Caption: "• REVERSIBLE WEAR"`,
`BOTTOM-RIGHT scene: Cat sleeping peacefully curled up with cone - Caption: "• 360° COMFORT"`,
`Add light paw print watermarks in corners of the overall image`
],
layoutDescription: `
- Background: Warm beige/cream (#F5EBE0)
- 2x2 grid of equal-sized rounded rectangles
- Each scene has the same cat in different poses
- Caption text below each scene in brown (#8B7355)
- Subtle paw prints in corners`,
styleGuide: `
Lifestyle photography style. Warm, inviting colors.
The cat should look comfortable and happy in all scenes.
Consistent lighting across all 4 scenes.
CRITICAL: The cat must be recognizably the SAME individual cat in all scenes.`,
aspectRatio: '1:1 square'
});
const result = await generateAIImage(prompt, wornUrl, '1:1');
await imageProcessor.saveImage(result, path.join(outputDir, 'Main_04.jpg'));
fs.writeFileSync(path.join(outputDir, 'Main_04_prompt.txt'), prompt);
return true;
}
/**
* Main_05: 尺寸图
*/
async function generateMain05(materials, config, outputDir) {
console.log('\n🎨 [Main_05] 尺寸图');
const flatBuffer = fs.readFileSync(materials.flatImage);
const flatUrl = await uploadToR2(flatBuffer, 'flat-size.png');
const prompt = buildEditPrompt({
subjectDescription: `An ice-blue TOUCHDOG soft pet recovery cone collar in flat lay position, showing its flower/petal shape design.`,
preserveElements: [
`The product MUST remain EXACTLY as shown: ice-blue color, flower shape, TOUCHDOG branding`,
`The product's proportions and shape`,
`The velcro closure and inner rim details`
],
editTasks: [
`Place product on warm beige background (#F5EDE4)`,
`Add title "PRODUCT SIZE" at top center in dark text`,
`Add a size chart table below the product with columns: SIZE | NECK | DEPTH`,
`Table data:
XS: 5.6-6.8IN | 3.2IN
S: 7.2-8.4IN | 4IN
M: 8.8-10.4IN | 5IN
L: 10.8-12.4IN | 6IN
XL: 12.8-14.4IN | 7IN`,
`Add note in coral/salmon color: "NOTE: ALWAYS MEASURE YOUR PET'S NECK BEFORE SELECTING A SIZE"`,
`Add subtle paw print decorations in beige tones`
],
layoutDescription: `
- Top: Title
- Center: Product image (main focus)
- Bottom: Size chart table with clean design`,
styleGuide: `Clean, informative infographic style. Easy to read table. Warm neutral background. Professional Amazon listing quality.`,
aspectRatio: '1:1 square'
});
const result = await generateAIImage(prompt, flatUrl, '1:1');
await imageProcessor.saveImage(result, path.join(outputDir, 'Main_05.jpg'));
fs.writeFileSync(path.join(outputDir, 'Main_05_prompt.txt'), prompt);
return true;
}
/**
* Main_02: 白底平铺+细节放大
*/
async function generateMain02(materials, config, outputDir) {
console.log('\n🎨 [Main_02] 白底平铺+细节放大');
const flatBuffer = fs.readFileSync(materials.flatImage);
const flatUrl = await uploadToR2(flatBuffer, 'flat-detail.png');
const prompt = buildEditPrompt({
subjectDescription: `An ice-blue TOUCHDOG soft pet recovery cone collar in flat lay position on black background. The product has a flower/petal shape with 8 segments, velcro closure on left, and green inner rim.`,
preserveElements: [
`The product MUST remain EXACTLY as shown: ice-blue color (#B5E5E8), flower shape, 8 petal segments`,
`The TOUCHDOG branding text on the product`,
`The velcro strap detail and green/teal inner rim`,
`The product's proportions and stitching pattern`
],
editTasks: [
`Change background from black to clean white`,
`Add a dark green (#2D4A3E) banner at top with text: "DURABLE WATERPROOF PU LAYER"`,
`Add 2 circular magnified detail callouts at bottom:
- Left circle: Close-up of outer material texture, label "DURABLE WATERPROOF PU LAYER"
- Right circle: Close-up of inner lining, label "DOUBLE-LAYER COMFORT"`,
`Add thin green borders around detail circles`,
`Keep layout clean and professional`
],
layoutDescription: `
- Top: Dark green banner with title
- Center: Main product image on white background
- Bottom: Two circular detail magnifications with labels`,
styleGuide: `Clean white background product photography. Professional Amazon main image style. High contrast, sharp details.`,
aspectRatio: '1:1 square'
});
const result = await generateAIImage(prompt, flatUrl, '1:1');
await imageProcessor.saveImage(result, path.join(outputDir, 'Main_02.jpg'));
fs.writeFileSync(path.join(outputDir, 'Main_02_prompt.txt'), prompt);
return true;
}
/**
* Main_06: 多角度展示
*/
async function generateMain06(materials, config, outputDir) {
console.log('\n🎨 [Main_06] 多角度展示');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'multi-angle.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat (white/cream fur, brown ear markings, blue eyes) wearing an ice-blue TOUCHDOG soft recovery cone collar.`,
preserveElements: [
`The cat MUST remain a Ragdoll: blue eyes, white/cream fur, brown markings`,
`The product MUST remain ice-blue (#B5E5E8), flower-shaped, TOUCHDOG branded`,
`Both views must show the EXACT SAME cat and product`
],
editTasks: [
`Create a split image with curved decorative divider in the middle`,
`LEFT SIDE: Front view of cat wearing the cone (from reference)`,
`RIGHT SIDE: Side/profile view of the same cat wearing the same cone`,
`Warm home interior background in both halves`,
`NO text overlays - pure lifestyle imagery`
],
layoutDescription: `
- Split layout with elegant curved divider
- Left: Front view
- Right: Side view
- Consistent warm lighting across both`,
styleGuide: `Lifestyle photography. Warm, cozy atmosphere. The cat should look comfortable. NO text.`,
aspectRatio: '1:1 square'
});
const result = await generateAIImage(prompt, wornUrl, '1:1');
await imageProcessor.saveImage(result, path.join(outputDir, 'Main_06.jpg'));
fs.writeFileSync(path.join(outputDir, 'Main_06_prompt.txt'), prompt);
return true;
}
/**
* APlus_01: 品牌横幅
*/
async function generateAPlus01(materials, config, outputDir) {
console.log('\n🎨 [APlus_01] 品牌横幅');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'brand-banner.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar in a home setting.`,
preserveElements: [
`The cat MUST remain a Ragdoll with blue eyes and white/cream fur`,
`The product MUST remain ice-blue, flower-shaped, TOUCHDOG branded`
],
editTasks: [
`Create a horizontal banner layout (3:2 ratio)`,
`LEFT 40%: Brand text area with:
- "TOUCHDOG" in playful coral/salmon color (#E8A87C)
- Small paw print icons around the brand name
- "CAT SOFT CONE COLLAR" below in gray`,
`RIGHT 60%: The cat wearing the product in a warm home setting`,
`Soft, warm color palette throughout`
],
layoutDescription: `
- Left side: Brand name and product title
- Right side: Hero image of cat with product
- Warm, cohesive color scheme`,
styleGuide: `Amazon A+ brand story style. Warm and inviting. Professional yet friendly.`,
aspectRatio: '3:2 landscape'
});
const result = await generateAIImage(prompt, wornUrl, '3:2');
await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_01.jpg'));
fs.writeFileSync(path.join(outputDir, 'APlus_01_prompt.txt'), prompt);
return true;
}
/**
* APlus_02: 对比图
*/
async function generateAPlus02(materials, config, outputDir) {
console.log('\n🎨 [APlus_02] 对比图');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'comparison.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar.`,
preserveElements: [
`LEFT SIDE: The cat and product MUST match the reference - Ragdoll cat, ice-blue soft cone`,
`The product color (#B5E5E8), flower shape, and TOUCHDOG branding on left side`
],
editTasks: [
`Create a side-by-side comparison layout`,
`LEFT SIDE (60% width, colorful):
- Happy Ragdoll cat wearing our ice-blue soft cone (from reference)
- Green checkmark icon
- "OUR" label on coral/orange banner
- 3 benefits in white: "CLOUD-LIGHT COMFORT", "WIDER & CLEARER", "FOLDABLE & PORTABLE"`,
`RIGHT SIDE (40% width, grayscale):
- Sad-looking cat wearing a DIFFERENT product: hard plastic transparent cone (traditional e-collar)
- Red X icon
- "OTHER" label on gray banner
- 3 drawbacks in gray: "HEAVY & BULKY", "BLOCKS VISION & MOVEMENT", "HARD TO STORE"`,
`Warm beige background with paw print watermarks`
],
layoutDescription: `
- Split layout with curved divider
- Left side larger, full color, positive mood
- Right side smaller, desaturated, negative mood
- Text overlays on each side`,
styleGuide: `
Clear visual contrast between "us" and "them".
Left side warm and inviting, right side cold and uncomfortable.
IMPORTANT: Right side must show a DIFFERENT type of cone (hard plastic), not our product.`,
aspectRatio: '3:2 landscape'
});
const result = await generateAIImage(prompt, wornUrl, '3:2');
await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_02.jpg'));
fs.writeFileSync(path.join(outputDir, 'APlus_02_prompt.txt'), prompt);
return true;
}
/**
* APlus_03: 功能细节展示
*/
async function generateAPlus03(materials, config, outputDir) {
console.log('\n🎨 [APlus_03] 功能细节展示');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'feature-detail.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar.`,
preserveElements: [
`The cat MUST remain a Ragdoll with blue eyes`,
`The product MUST remain ice-blue, flower-shaped, TOUCHDOG branded`
],
editTasks: [
`Create horizontal layout (3:2) with title and 3 detail panels`,
`Title at top: "ENGINEERED FOR UNCOMPROMISED COMFORT" in dark text`,
`3 detail images in a row below:
- Panel 1: Close-up of inner cotton lining texture, caption "STURDY AND BREATHABLE"
- Panel 2: Cat wearing the product looking comfortable, caption "EASY TO CLEAN, STYLISH"
- Panel 3: Close-up of stitching detail, caption "REINFORCED STITCHING"`,
`Warm beige background (#F5EBE0) with subtle paw prints`
],
layoutDescription: `
- Top: Title banner
- Bottom: Three equal-width panels with captions`,
styleGuide: `Amazon A+ feature module style. Clean, informative. Warm color palette.`,
aspectRatio: '3:2 landscape'
});
const result = await generateAIImage(prompt, wornUrl, '3:2');
await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_03.jpg'));
fs.writeFileSync(path.join(outputDir, 'APlus_03_prompt.txt'), prompt);
return true;
}
/**
* APlus_04: 多场景横版
*/
async function generateAPlus04(materials, config, outputDir) {
console.log('\n🎨 [APlus_04] 多场景横版');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'scenes-horizontal.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat (white/cream fur, blue eyes, brown ear markings) wearing an ice-blue TOUCHDOG soft recovery cone collar.`,
preserveElements: [
`ALL 4 SCENES MUST SHOW THE EXACT SAME CAT: Ragdoll breed, blue eyes, white/cream fur`,
`ALL 4 SCENES MUST SHOW THE EXACT SAME PRODUCT: Ice-blue flower-shaped soft cone`,
`Consistent cat appearance and product across all panels`
],
editTasks: [
`Create horizontal layout (3:2) with 4 scenes in a row`,
`Scene 1: Cat standing - "HYGIENIC & EASY TO CLEAN"`,
`Scene 2: Cat eating from bowl - "UNRESTRICTED EATING"`,
`Scene 3: Cat playing/stretching - "REVERSIBLE WEAR"`,
`Scene 4: Cat sleeping curled up - "360° COMFORT"`,
`Each scene in a rounded rectangle frame`,
`Warm beige background with paw print decorations`
],
layoutDescription: `
- 4 equal panels arranged horizontally
- Each panel has image + caption below
- Consistent warm lighting across all`,
styleGuide: `Lifestyle photography. Same cat in all scenes. Warm, cozy feel.`,
aspectRatio: '3:2 landscape'
});
const result = await generateAIImage(prompt, wornUrl, '3:2');
await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_04.jpg'));
fs.writeFileSync(path.join(outputDir, 'APlus_04_prompt.txt'), prompt);
return true;
}
/**
* APlus_05: 多角度横版
*/
async function generateAPlus05(materials, config, outputDir) {
console.log('\n🎨 [APlus_05] 多角度横版');
const wornBuffer = fs.readFileSync(materials.catWornImage);
const wornUrl = await uploadToR2(wornBuffer, 'angles-horizontal.jpg');
const prompt = buildEditPrompt({
subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar.`,
preserveElements: [
`Both views must show the SAME Ragdoll cat with blue eyes`,
`Both views must show the SAME ice-blue TOUCHDOG cone`
],
editTasks: [
`Create horizontal split layout (3:2)`,
`LEFT: Front view of cat wearing cone`,
`RIGHT: Side/profile view of same cat with cone`,
`Elegant curved divider between the two views`,
`Warm home background in both`,
`NO text - pure visual showcase`
],
layoutDescription: `
- Two equal halves with curved divider
- Left: Front angle
- Right: Side angle
- Warm consistent lighting`,
styleGuide: `High-end lifestyle photography. No text. Warm atmosphere.`,
aspectRatio: '3:2 landscape'
});
const result = await generateAIImage(prompt, wornUrl, '3:2');
await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_05.jpg'));
fs.writeFileSync(path.join(outputDir, 'APlus_05_prompt.txt'), prompt);
return true;
}
/**
* APlus_06: 尺寸表横版
* 优化明确描述产品的C形开口特征
*/
async function generateAPlus06(materials, config, outputDir) {
console.log('\n🎨 [APlus_06] 尺寸表横版');
const flatBuffer = fs.readFileSync(materials.flatImage);
const flatUrl = await uploadToR2(flatBuffer, 'size-chart-h.png');
const prompt = buildEditPrompt({
subjectDescription: `An ice-blue TOUCHDOG soft pet recovery cone collar in flat lay position.
CRITICAL SHAPE DETAIL: The product has a C-SHAPED OPENING (not a closed circle) - there is a GAP on the left side where the velcro strap attaches. This opening allows the collar to wrap around the pet's neck.`,
preserveElements: [
`The product MUST keep its C-SHAPED OPENING - DO NOT close the gap into a full circle`,
`The velcro strap visible on the left side of the opening`,
`Ice-blue color (#B5E5E8), flower/petal shape with 8 segments`,
`TOUCHDOG brand text on the product`,
`Green/teal inner rim around the neck hole`
],
editTasks: [
`Change background from black to warm beige (#F5EDE4)`,
`Add title "PRODUCT SIZE" at top center in dark text`,
`Add dimension labels: "NECK" pointing to inner circle, "DEPTH" pointing outward`,
`Add size chart table on the right:
SIZE | NECK | DEPTH
XS | 5.6-6.8IN | 3.2IN
S | 7.2-8.4IN | 4IN
M | 8.8-10.4IN | 5IN
L | 10.8-12.4IN | 6IN
XL | 12.8-14.4IN | 7IN`,
`Add note in coral: "ALWAYS MEASURE YOUR PET'S NECK BEFORE SELECTING A SIZE"`,
`Add subtle paw print watermarks`
],
layoutDescription: `
- Left 45%: Product image maintaining C-shape opening
- Right 55%: Size chart table
- Top: Title
- Bottom: Note text`,
styleGuide: `Clean infographic style. The product's C-shaped opening must be clearly visible - this is a key feature showing how it wraps around the neck.`,
aspectRatio: '3:2 landscape'
});
const result = await generateAIImage(prompt, flatUrl, '3:2');
await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_06.jpg'));
fs.writeFileSync(path.join(outputDir, 'APlus_06_prompt.txt'), prompt);
return true;
}
// ============================================================
// 主流程
// ============================================================
async function main() {
const args = process.argv.slice(2);
let materialDir = path.join(__dirname, '素材/素材/已有的素材');
let skuName = 'Touchdog冰蓝色伊丽莎白圈';
// 可选:只生成指定的图
let onlyGenerate = null; // e.g., ['Main_01', 'Main_04']
for (const arg of args) {
if (arg.startsWith('--material-dir=')) {
materialDir = arg.split('=')[1];
} else if (arg.startsWith('--sku-name=')) {
skuName = arg.split('=')[1];
} else if (arg.startsWith('--only=')) {
onlyGenerate = arg.split('=')[1].split(',');
}
}
const timestamp = new Date().toISOString().slice(0, 10);
const timeStr = new Date().toTimeString().slice(0, 8).replace(/:/g, '-');
const safeName = skuName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_').slice(0, 20);
const outputDir = path.join(__dirname, `output_v6_${safeName}_${timestamp}-${timeStr}`);
console.log('\n' + '═'.repeat(70));
console.log('🚀 POC Workflow V6 - 优化Prompt控制策略');
console.log('═'.repeat(70));
console.log('\n📋 核心策略: 精准Prompt控制明确保留元素限定编辑范围');
console.log(' 素材目录:', materialDir);
console.log(' 输出目录:', outputDir);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 扫描素材
const files = fs.readdirSync(materialDir);
const images = files.filter(f => /\.(jpg|jpeg|png)$/i.test(f));
console.log('\n📁 可用素材:');
images.forEach(img => console.log(` - ${img}`));
// ========== 修复:正确识别素材 ==========
// 平铺图IMG_5683.png黑底抠图
const flatImage = images.find(i => i.includes('5683')) || images.find(i => i.endsWith('.png'));
// 猫咪佩戴图IMG_6216 或 IMG_6229不是6514的狗狗图
const catWornImage = images.find(i => i.includes('6216')) ||
images.find(i => i.includes('6229')) ||
images.find(i => i.toLowerCase().includes('cat'));
// 狗狗佩戴图(备用)
const dogWornImage = images.find(i => i.includes('6514'));
if (!flatImage) {
console.error('❌ 找不到平铺图素材');
return;
}
if (!catWornImage) {
console.error('❌ 找不到猫咪佩戴图素材');
console.log(' 可用文件:', images);
return;
}
const materials = {
flatImage: path.join(materialDir, flatImage),
catWornImage: path.join(materialDir, catWornImage),
dogWornImage: dogWornImage ? path.join(materialDir, dogWornImage) : null
};
console.log('\n✅ 素材选择:');
console.log(' 平铺图:', flatImage);
console.log(' 猫咪佩戴图:', catWornImage, '← 使用这个');
if (dogWornImage) console.log(' 狗狗佩戴图:', dogWornImage, '(备用)');
const config = { brandName: 'TOUCHDOG', productName: 'CAT SOFT CONE COLLAR' };
const results = [];
// ========== 生成图片 ==========
console.log('\n' + '─'.repeat(70));
console.log('🎨 开始生成图片');
console.log('─'.repeat(70));
const generators = {
'Main_01': () => generateMain01(materials, config, outputDir),
'Main_02': () => generateMain02(materials, config, outputDir),
'Main_03': () => generateMain03(materials, config, outputDir),
'Main_04': () => generateMain04(materials, config, outputDir),
'Main_05': () => generateMain05(materials, config, outputDir),
'Main_06': () => generateMain06(materials, config, outputDir),
'APlus_01': () => generateAPlus01(materials, config, outputDir),
'APlus_02': () => generateAPlus02(materials, config, outputDir),
'APlus_03': () => generateAPlus03(materials, config, outputDir),
'APlus_04': () => generateAPlus04(materials, config, outputDir),
'APlus_05': () => generateAPlus05(materials, config, outputDir),
'APlus_06': () => generateAPlus06(materials, config, outputDir),
};
for (const [id, generator] of Object.entries(generators)) {
if (onlyGenerate && !onlyGenerate.includes(id)) {
console.log(`\n⏭️ [${id}] 跳过`);
continue;
}
try {
results.push({ id, success: await generator() });
} catch (e) {
console.error(`\n${id} 失败:`, e.message);
results.push({ id, success: false, error: e.message });
}
}
// ========== 总结 ==========
console.log('\n' + '═'.repeat(70));
console.log('📊 生成完成');
console.log('═'.repeat(70));
const successCount = results.filter(r => r.success).length;
console.log(`\n✅ 成功: ${successCount}/${results.length}`);
if (successCount < results.length) {
console.log('\n❌ 失败:');
results.filter(r => !r.success).forEach(r => console.log(` - ${r.id}: ${r.error}`));
}
console.log(`\n📁 输出目录: ${outputDir}`);
console.log(' 每张图都保存了对应的 _prompt.txt 供调试');
// 保存所有prompt到一个文件
const allPrompts = {};
for (const id of Object.keys(generators)) {
const promptFile = path.join(outputDir, `${id}_prompt.txt`);
if (fs.existsSync(promptFile)) {
allPrompts[id] = fs.readFileSync(promptFile, 'utf-8');
}
}
fs.writeFileSync(path.join(outputDir, 'all-prompts.json'), JSON.stringify(allPrompts, null, 2));
fs.writeFileSync(path.join(outputDir, 'results.json'), JSON.stringify(results, null, 2));
}
main().catch(console.error);