157 lines
6.9 KiB
TypeScript
157 lines
6.9 KiB
TypeScript
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
import { convertImageVton, convertImage } from '../lib/image-converter';
|
||
import * as dotenv from 'dotenv';
|
||
import sharp from 'sharp';
|
||
|
||
dotenv.config();
|
||
|
||
const clothesDir = 'D:\\projects\\random_video_maker\\input';
|
||
const outputDir = 'generated';
|
||
|
||
const comfyBaseUrl = process.env.SERVER1_COMFY_BASE_URL;
|
||
const comfyOutputDir = process.env.SERVER1_COMFY_OUTPUT_DIR;
|
||
|
||
function getNextIndex(directory: string): number {
|
||
if (!fs.existsSync(directory)) {
|
||
fs.mkdirSync(directory, { recursive: true });
|
||
return 0;
|
||
}
|
||
const dirs = fs.readdirSync(directory, { withFileTypes: true })
|
||
.filter(dirent => dirent.isDirectory())
|
||
.map(dirent => dirent.name);
|
||
|
||
const vtonDirs = dirs.filter(dir => dir.startsWith('vton_'));
|
||
if (vtonDirs.length === 0) {
|
||
return 0;
|
||
}
|
||
const indices = vtonDirs.map(dir => {
|
||
const match = dir.match(/vton_(\d+)/);
|
||
return match ? parseInt(match[1], 10) : -1;
|
||
});
|
||
return Math.max(...indices) + 1;
|
||
}
|
||
|
||
function getRandomFile(directory: string): string {
|
||
const files = fs.readdirSync(directory).filter(file => /\.(jpg|png|jpeg)$/i.test(file));
|
||
if (files.length === 0) {
|
||
throw new Error(`No image files found in directory: ${directory}`);
|
||
}
|
||
const randomFile = files[Math.floor(Math.random() * files.length)];
|
||
return path.join(directory, randomFile);
|
||
}
|
||
|
||
async function generateVtonImages() {
|
||
if (!comfyBaseUrl || !comfyOutputDir) {
|
||
throw new Error("ComfyUI URL or Output Directory is not set in environment variables.");
|
||
}
|
||
|
||
let index = getNextIndex(outputDir);
|
||
|
||
const comfyInputDir = comfyOutputDir.replace("output", "input");
|
||
|
||
while (true) { // Infinite loop
|
||
const iterationDir = path.join(outputDir, `vton_${index}`);
|
||
fs.mkdirSync(iterationDir, { recursive: true });
|
||
|
||
try {
|
||
const personOrigPath = getRandomFile(clothesDir);
|
||
const clothOrigPath = getRandomFile(clothesDir);
|
||
|
||
fs.copyFileSync(personOrigPath, path.join(iterationDir, '1-personOrig.png'));
|
||
fs.copyFileSync(clothOrigPath, path.join(iterationDir, '3-clothOrig.png'));
|
||
|
||
const personOrigFileName = path.basename(personOrigPath);
|
||
const clothOrigFileName = path.basename(clothOrigPath);
|
||
|
||
fs.copyFileSync(personOrigPath, path.join(comfyInputDir, personOrigFileName));
|
||
fs.copyFileSync(clothOrigPath, path.join(comfyInputDir, clothOrigFileName));
|
||
|
||
console.log(`Processing person: ${personOrigPath}, cloth: ${clothOrigPath}`);
|
||
|
||
const cleanePersonImagePath = await convertImage("请把姿势改成站立的,转换成全身照片。去掉衣服,只保留白色运动文胸和白色短裤。双脚保持赤脚。背景为浅灰色。", personOrigFileName, comfyBaseUrl, comfyOutputDir, { width: 720, height: 1280 });
|
||
fs.copyFileSync(cleanePersonImagePath, path.join(iterationDir, '2-personCleaned.png'));
|
||
const cleanedPersonFileName = path.basename(cleanePersonImagePath);
|
||
fs.copyFileSync(cleanePersonImagePath, path.join(comfyInputDir, cleanedPersonFileName));
|
||
|
||
const cleanedClothImagePath = await convertImage("请将图1中的上衣、下装和配饰分别提取出来,放到同一个浅灰色的背景上。", clothOrigFileName, comfyBaseUrl, comfyOutputDir, { width: 720, height: 1280 });
|
||
fs.copyFileSync(cleanedClothImagePath, path.join(iterationDir, '4-clothCleaned.png'));
|
||
const cleanedClothFileName = path.basename(cleanedClothImagePath);
|
||
fs.copyFileSync(cleanedClothImagePath, path.join(comfyInputDir, cleanedClothFileName));
|
||
|
||
const outputFilename = `vton_final_${index}.png`;
|
||
const generatedImagePath = await convertImageVton(cleanedPersonFileName, cleanedClothFileName, outputFilename, comfyBaseUrl, comfyOutputDir, { width: 720, height: 1280 });
|
||
|
||
if (generatedImagePath) {
|
||
fs.copyFileSync(generatedImagePath, path.join(iterationDir, '5-finalResult.png'));
|
||
console.log(`Generated image saved to ${generatedImagePath}`);
|
||
|
||
// --- Create composite image ---
|
||
const imagePaths = [
|
||
path.join(iterationDir, '1-personOrig.png'),
|
||
path.join(iterationDir, '3-clothOrig.png'),
|
||
path.join(iterationDir, '2-personCleaned.png'),
|
||
path.join(iterationDir, '4-clothCleaned.png'),
|
||
path.join(iterationDir, '5-finalResult.png')
|
||
];
|
||
|
||
const resizedImages = [];
|
||
let totalWidth = 10; // Initial left margin
|
||
const resizedHeight = 720;
|
||
|
||
for (const imagePath of imagePaths) {
|
||
const image = sharp(imagePath);
|
||
const metadata = await image.metadata();
|
||
if (!metadata.width || !metadata.height) {
|
||
throw new Error(`Could not get metadata for image ${imagePath}`);
|
||
}
|
||
const resizedWidth = Math.round((metadata.width / metadata.height) * resizedHeight);
|
||
|
||
const resizedImageBuffer = await image.resize(resizedWidth, resizedHeight).toBuffer();
|
||
resizedImages.push({
|
||
buffer: resizedImageBuffer,
|
||
width: resizedWidth
|
||
});
|
||
totalWidth += resizedWidth + 10; // Add image width and right margin
|
||
}
|
||
|
||
const compositeOps = [];
|
||
let currentLeft = 10; // Start with left margin
|
||
|
||
for (const img of resizedImages) {
|
||
compositeOps.push({
|
||
input: img.buffer,
|
||
top: 10, // 10px top margin
|
||
left: currentLeft
|
||
});
|
||
currentLeft += img.width + 10; // Move to the next position
|
||
}
|
||
|
||
await sharp({
|
||
create: {
|
||
width: totalWidth,
|
||
height: 740,
|
||
channels: 4,
|
||
background: { r: 255, g: 255, b: 255, alpha: 1 }
|
||
}
|
||
})
|
||
.composite(compositeOps)
|
||
.toFile(path.join(iterationDir, 'process.png'));
|
||
|
||
console.log(`Generated composite image process.png in ${iterationDir}`);
|
||
// --- End of composite image creation ---
|
||
|
||
index++;
|
||
} else {
|
||
console.error(`Failed to generate image for index ${index}`);
|
||
}
|
||
} catch (error) {
|
||
console.error("An error occurred during image generation:", error);
|
||
// Optional: wait for a bit before retrying to avoid spamming errors
|
||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||
}
|
||
}
|
||
}
|
||
|
||
generateVtonImages().catch(console.error);
|