170 lines
6.3 KiB
TypeScript
170 lines
6.3 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { callLMStudioAPIWithFile, callLmstudio } from '../lib/lmstudio';
|
|
import { embedJsonToPng } from '../lib/util';
|
|
import { downloadImagesFromPinterestSearch } from '../lib/pinterest';
|
|
import { logger } from '../lib/logger';
|
|
import sharp from 'sharp';
|
|
|
|
const INPUT_DIR = path.join(process.cwd(), 'input');
|
|
const OUTPUT_DIR = path.join(process.cwd(), 'generated', 'prompts');
|
|
|
|
if (!fs.existsSync(OUTPUT_DIR)) {
|
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
}
|
|
|
|
async function generatePromptsForImage(imagePath: string, index: number) {
|
|
const outputFilePath = path.join(OUTPUT_DIR, `cleaned_prompt_${index}.png`);
|
|
logger.info(`Processing image: ${path.basename(imagePath)} -> ${path.basename(outputFilePath)}`);
|
|
|
|
try {
|
|
// Step 1: Detect main object and generate colors from the input image
|
|
const colorGenerationPrompt = `
|
|
You are a creative assistant. Analyze the provided image.
|
|
Identify the main subject product ( not a product name).
|
|
Then, list exactly five colors related to this subject:
|
|
- Two colors that are common for this object.
|
|
- Two colors that are uncommon but plausible.
|
|
- One color that is completely crazy or surreal for this object.
|
|
|
|
Output strictly in this JSON format:
|
|
{
|
|
"result": {
|
|
"main_object": "the identified noun",
|
|
"colors": [
|
|
"color1",
|
|
"color2",
|
|
"color3",
|
|
"color4",
|
|
"color5"
|
|
]
|
|
}
|
|
}
|
|
`;
|
|
const colorResponse = await callLMStudioAPIWithFile(imagePath, colorGenerationPrompt);
|
|
const { main_object, colors } = colorResponse.result;
|
|
|
|
if (!main_object || !Array.isArray(colors) || colors.length !== 5) {
|
|
logger.error(`Failed to get a valid main object and color list for ${imagePath}.`);
|
|
return;
|
|
}
|
|
|
|
logger.info(`Main object: "${main_object}", Colors: ${colors.join(', ')}`);
|
|
|
|
const prompts: { imagePrompt: string, videoPrompt: string }[] = [];
|
|
const themes = ["special", "unique", "beautiful", "crazy", "funny"];
|
|
|
|
// Step 2: Iterate through each color
|
|
for (const color of colors) {
|
|
const randomTheme = themes[Math.floor(Math.random() * themes.length)];
|
|
const pinterestQuery = `${main_object} product photo ${color} background ${randomTheme}`;
|
|
logger.info(`Searching Pinterest for: "${pinterestQuery}"`);
|
|
|
|
// Step 3: Get an image from Pinterest
|
|
const downloadedImages = await downloadImagesFromPinterestSearch(pinterestQuery, 1);
|
|
if (downloadedImages.length === 0) {
|
|
logger.warn(`Could not find an image on Pinterest for query: "${pinterestQuery}"`);
|
|
continue;
|
|
}
|
|
const pinterestImagePath = downloadedImages[0];
|
|
logger.info(`Downloaded Pinterest image: ${pinterestImagePath}`);
|
|
|
|
// Step 4: Generate a detailed prompt from the Pinterest image
|
|
const imagePromptRequest = `
|
|
You are an expert in generating descriptive prompts for image generation models.
|
|
Analyze the provided image and describe it in a single, detailed paragraph.
|
|
Focus on style, mood, lighting, color palette, sub-objects, and composition.
|
|
Do not mention the main object itself. The prompt should be about the scene.
|
|
|
|
Output strictly in this JSON format:
|
|
{
|
|
"result": "your generated prompt here"
|
|
}
|
|
`;
|
|
const imagePromptResponse = await callLMStudioAPIWithFile(pinterestImagePath, imagePromptRequest);
|
|
const imagePrompt = imagePromptResponse.result;
|
|
|
|
if (imagePrompt) {
|
|
logger.info(`Generated image prompt for color ${color}: "${imagePrompt}"`);
|
|
|
|
// Step 5: Generate a matching video prompt
|
|
const videoPromptRequest = `
|
|
You are a creative director for a short, stylish video ad.
|
|
Based on the provided image and the following scene description, generate an attractive video prompt.
|
|
|
|
Main Subject: ${main_object}
|
|
Scene Description: ${imagePrompt}
|
|
|
|
The video prompt should:
|
|
- Be in English and approximately 50 words.
|
|
- Describe one clear action involving the main subject.
|
|
- Include one specific camera movement (e.g., slow zoom in, orbiting shot, push-in, pull-out).
|
|
- Be dynamic and visually appealing.
|
|
|
|
Output strictly in this JSON format:
|
|
{
|
|
"result": "your generated video prompt here"
|
|
}
|
|
`;
|
|
const videoPromptResponse = await callLMStudioAPIWithFile(pinterestImagePath, videoPromptRequest);
|
|
const videoPrompt = videoPromptResponse.result;
|
|
|
|
if (videoPrompt) {
|
|
logger.info(`Generated video prompt for color ${color}: "${videoPrompt}"`);
|
|
prompts.push({ imagePrompt, videoPrompt });
|
|
} else {
|
|
logger.warn(`Failed to generate a video prompt for ${pinterestImagePath}`);
|
|
}
|
|
} else {
|
|
logger.warn(`Failed to generate an image prompt for ${pinterestImagePath}`);
|
|
}
|
|
}
|
|
|
|
if (prompts.length === 0) {
|
|
logger.error(`No prompt pairs were generated for ${imagePath}. Aborting.`);
|
|
return;
|
|
}
|
|
|
|
// Step 6: Embed all prompts into the original image and save to the new location
|
|
const metadata = {
|
|
prompts: prompts
|
|
};
|
|
|
|
// Convert original image to a valid PNG at the output path before embedding
|
|
await sharp(imagePath)
|
|
.toFormat('png')
|
|
.toFile(outputFilePath);
|
|
|
|
await embedJsonToPng(outputFilePath, metadata);
|
|
|
|
logger.info(`Successfully generated prompts and saved metadata to ${outputFilePath}`);
|
|
|
|
} catch (error) {
|
|
logger.error(`An error occurred while processing ${imagePath}:`, error);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
const files = fs.readdirSync(INPUT_DIR);
|
|
const imageFiles = files.filter(file => /\.(png|jpg|jpeg)$/i.test(file));
|
|
|
|
if (imageFiles.length === 0) {
|
|
console.log('No images found in the input directory.');
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < imageFiles.length; i++) {
|
|
const imageFile = imageFiles[i];
|
|
const imagePath = path.join(INPUT_DIR, imageFile);
|
|
await generatePromptsForImage(imagePath, i);
|
|
}
|
|
|
|
console.log('All images processed.');
|
|
} catch (error) {
|
|
console.error('An error occurred in the main process:', error);
|
|
}
|
|
}
|
|
|
|
main();
|