Carrier workflow + 4K upscale scripts
This commit is contained in:
214
poc-workflow-carrier-v3.js
Normal file
214
poc-workflow-carrier-v3.js
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* POC Workflow Carrier V3: 高保真、零推理工作流
|
||||
*
|
||||
* 核心策略:
|
||||
* 1. 强力约束:锁定五金细节、文字大小写、精确尺寸数据。
|
||||
* 2. 视觉统一:强制 AI 使用统一背景颜色、字体风格和标注样式。
|
||||
* 3. 素材绑定:每张图绑定最准确的源文件,减少 AI “脑补”。
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
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';
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 统一视觉标准 (Global UI Standards)
|
||||
// ============================================================
|
||||
const UI_STANDARDS = {
|
||||
font: "Modern geometric sans-serif font (OPPOSans style), clean and highly readable",
|
||||
background: "Clean unified soft beige gradient background (#F5EDE4 to #FFFFFF)",
|
||||
calloutStyle: "Minimalist thin solid lines with small dots, professional and tidy",
|
||||
brandText: "touchdog (ALWAYS LOWERCASE, no exceptions)",
|
||||
dimensions: "L: 15.35\" x W: 6.1\" x H: 8.27\" (Height: 28 cm)"
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// 工具函数
|
||||
// ============================================================
|
||||
|
||||
async function uploadToR2(buffer, filename) {
|
||||
const fileName = `carrier-v3-${Date.now()}-${filename}`;
|
||||
await r2Client.send(new PutObjectCommand({
|
||||
Bucket: process.env.R2_BUCKET_NAME || 'ai-flow',
|
||||
Key: fileName,
|
||||
Body: buffer,
|
||||
ContentType: 'image/jpeg',
|
||||
}));
|
||||
return `${process.env.R2_PUBLIC_DOMAIN}/${fileName}`;
|
||||
}
|
||||
|
||||
async function submitTask(prompt, imgUrl, aspectRatio) {
|
||||
const payload = {
|
||||
key: API_KEY,
|
||||
prompt: prompt,
|
||||
img_url: imgUrl,
|
||||
aspectRatio: aspectRatio,
|
||||
imageSize: '4K' // 维持 4K 高清
|
||||
};
|
||||
const response = await axios.post(`${API_BASE}/img/nanoBanana-pro`, payload, { timeout: 60000 });
|
||||
return response.data.data?.id || response.data.id;
|
||||
}
|
||||
|
||||
async function pollResult(taskId) {
|
||||
let attempts = 0;
|
||||
while (attempts < 100) {
|
||||
const res = await axios.get(`${API_BASE}/img/drawDetail`, { params: { key: API_KEY, id: taskId } });
|
||||
if (res.data.data?.status === 2) return res.data.data.image_url;
|
||||
if (res.data.data?.status === 3) throw new Error('Failed: ' + res.data.data.fail_reason);
|
||||
process.stdout.write('.');
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
attempts++;
|
||||
}
|
||||
throw new Error('Timeout');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// V3 高保真 Prompt 构建器
|
||||
// ============================================================
|
||||
|
||||
function buildFidelityPrompt(config) {
|
||||
return `
|
||||
[IMAGE EDITING TASK - STRICT FIDELITY MODE]
|
||||
|
||||
1. REFERENCE ANALYSIS:
|
||||
Source product is a luxury pet carrier.
|
||||
KEY DETAIL: The front pockets use SILVER TUCK-LOCK CLASPS (round/oval metal locks), NOT prong buckles.
|
||||
KEY DETAIL: Brand logo is "touchdog" in LOWERCASE.
|
||||
|
||||
2. CRITICAL PRESERVATION (DO NOT MODIFY PRODUCT):
|
||||
- KEEP the exact hardware style: Silver metal tuck-lock clasps.
|
||||
- KEEP the "touchdog" logo in LOWERCASE.
|
||||
- KEEP the diamond quilted texture and mocha brown color (#59483D).
|
||||
- DO NOT add side pockets if they are not in the reference.
|
||||
|
||||
3. EDITING TASKS:
|
||||
${config.tasks.join('\n')}
|
||||
|
||||
4. UI & STYLE STANDARDS:
|
||||
- Background: ${UI_STANDARDS.background}
|
||||
- Font: ${UI_STANDARDS.font}
|
||||
- All text must be in English.
|
||||
- Use ${UI_STANDARDS.calloutStyle} for any annotations.
|
||||
|
||||
5. TEXT CONTENT:
|
||||
${config.textContent.join('\n')}
|
||||
|
||||
OUTPUT: 4K High Definition, Professional Amazon US Listing Style.
|
||||
`.trim();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 任务执行逻辑
|
||||
// ============================================================
|
||||
|
||||
async function runV3() {
|
||||
const materialDir = path.join(__dirname, '素材/登机包');
|
||||
const outputDir = path.join(__dirname, `output_carrier_v3_Final_${new Date().toISOString().slice(0, 10)}`);
|
||||
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
console.log('🚀 启动 V3 高保真生成工作流 (对齐客户反馈)...');
|
||||
|
||||
const tasks = [
|
||||
{
|
||||
id: 'APlus_02',
|
||||
source: 'P1191464.JPG', // 侧面结构图
|
||||
config: {
|
||||
tasks: [
|
||||
"Place the bag under an airplane seat in a high-end cabin setting.",
|
||||
"Add airline approval icons."
|
||||
],
|
||||
textContent: [
|
||||
"Title: AIRLINE APPROVED",
|
||||
"Bullet 1: Height: 28 cm (Fits Most Airlines)",
|
||||
"Bullet 2: TSA Compliant Design"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'APlus_03',
|
||||
source: '1023_0151.JPG', // 特写图(有正确扣件)
|
||||
config: {
|
||||
tasks: [
|
||||
"Maintain the split-screen detail layout.",
|
||||
"Enhance the water-shedding droplets on the left side.",
|
||||
"Sharpen the silver tuck-lock clasp on the right side."
|
||||
],
|
||||
textContent: [
|
||||
"Logo: touchdog (LOWERCASE)",
|
||||
"Main Text: LUXURIOUS FEEL & STYLISH DESIGN",
|
||||
"Subtext: Premium textured finish with light-luxury sheen"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Main_02',
|
||||
source: 'P1191451.JPG', // 侧面透气图
|
||||
config: {
|
||||
tasks: [
|
||||
"Highlight the privacy curtains and mesh windows.",
|
||||
"Add clean blue airflow arrows.",
|
||||
"Replace background with unified soft beige gradient.",
|
||||
"CRITICAL: REMOVE the short carrying handle on top of the bag. The top should be clean and flat without any short briefcase-style handle."
|
||||
],
|
||||
textContent: [
|
||||
"Text: 360° BREATHABLE COMFORT",
|
||||
"Label: Adjustable Privacy Curtains",
|
||||
"Label: Dual 2.5cm Vent Holes"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Main_05',
|
||||
source: 'P1191464.JPG', // 正面图用于尺寸
|
||||
config: {
|
||||
tasks: [
|
||||
"Place bag on unified white/beige background.",
|
||||
"Add dimension lines with precision."
|
||||
],
|
||||
textContent: [
|
||||
`Dimensions: ${UI_STANDARDS.dimensions}`,
|
||||
"Text: FITS UNDER SEAT",
|
||||
"Weight: 0.56 KG Lightweight"
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
for (const task of tasks) {
|
||||
console.log(`\n🎨 正在处理 [${task.id}] 使用素材: ${task.source}`);
|
||||
try {
|
||||
const prompt = buildFidelityPrompt(task.config);
|
||||
const imgBuffer = fs.readFileSync(path.join(materialDir, task.source));
|
||||
const imgUrl = await uploadToR2(imgBuffer, task.source);
|
||||
|
||||
const taskId = await submitTask(prompt, imgUrl, task.id.startsWith('APlus') ? '3:2' : '1:1');
|
||||
const resultUrl = await pollResult(taskId);
|
||||
|
||||
const resultBuffer = await axios.get(resultUrl, { responseType: 'arraybuffer' });
|
||||
fs.writeFileSync(path.join(outputDir, `${task.id}.jpg`), Buffer.from(resultBuffer.data));
|
||||
fs.writeFileSync(path.join(outputDir, `${task.id}_prompt.txt`), prompt);
|
||||
console.log(`✅ ${task.id} 成功`);
|
||||
} catch (e) {
|
||||
console.error(`❌ ${task.id} 失败: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runV3().catch(console.error);
|
||||
|
||||
Reference in New Issue
Block a user