Carrier workflow + 4K upscale scripts

This commit is contained in:
tony
2026-01-07 10:12:38 +08:00
parent f5cb1042ae
commit 578a360894
6 changed files with 1185 additions and 0 deletions

214
poc-workflow-carrier-v3.js Normal file
View 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);