337 lines
11 KiB
TypeScript
337 lines
11 KiB
TypeScript
/**
|
|
* OpenAI Images API - Image Editing Examples (GPT-Image-1)
|
|
*
|
|
* This template demonstrates:
|
|
* - Basic image editing
|
|
* - Compositing multiple images
|
|
* - Transparent backgrounds
|
|
* - Different output formats
|
|
* - Compression settings
|
|
*
|
|
* NOTE: Image editing uses multipart/form-data, not JSON
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import FormData from 'form-data';
|
|
|
|
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
|
|
// =============================================================================
|
|
// BASIC IMAGE EDITING
|
|
// =============================================================================
|
|
|
|
async function basicEdit() {
|
|
const formData = new FormData();
|
|
formData.append('model', 'gpt-image-1');
|
|
formData.append('image', fs.createReadStream('./input-image.jpg'));
|
|
formData.append('prompt', 'Change the sky to a sunset with orange and pink colors');
|
|
formData.append('size', '1024x1024');
|
|
|
|
const response = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...formData.getHeaders(),
|
|
},
|
|
body: formData,
|
|
});
|
|
|
|
const data: any = await response.json();
|
|
console.log('Edited image URL:', data.data[0].url);
|
|
|
|
return data.data[0].url;
|
|
}
|
|
|
|
// =============================================================================
|
|
// COMPOSITE TWO IMAGES
|
|
// =============================================================================
|
|
|
|
async function compositeImages() {
|
|
const formData = new FormData();
|
|
formData.append('model', 'gpt-image-1');
|
|
formData.append('image', fs.createReadStream('./woman.jpg'));
|
|
formData.append('image_2', fs.createReadStream('./logo.png'));
|
|
formData.append('prompt', 'Add the logo to the woman\'s top, as if stamped into the fabric.');
|
|
formData.append('input_fidelity', 'high'); // Stay close to original
|
|
formData.append('size', '1024x1024');
|
|
|
|
const response = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...formData.getHeaders(),
|
|
},
|
|
body: formData,
|
|
});
|
|
|
|
const data: any = await response.json();
|
|
console.log('Composite image URL:', data.data[0].url);
|
|
|
|
return data.data[0].url;
|
|
}
|
|
|
|
// =============================================================================
|
|
// REMOVE BACKGROUND (Transparent)
|
|
// =============================================================================
|
|
|
|
async function removeBackground() {
|
|
const formData = new FormData();
|
|
formData.append('model', 'gpt-image-1');
|
|
formData.append('image', fs.createReadStream('./product.jpg'));
|
|
formData.append('prompt', 'Remove the background, keeping only the product.');
|
|
formData.append('format', 'png'); // Required for transparency
|
|
formData.append('background', 'transparent');
|
|
formData.append('size', '1024x1024');
|
|
|
|
const response = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...formData.getHeaders(),
|
|
},
|
|
body: formData,
|
|
});
|
|
|
|
const data: any = await response.json();
|
|
console.log('Transparent background URL:', data.data[0].url);
|
|
|
|
// Download and save
|
|
const imageResponse = await fetch(data.data[0].url);
|
|
const buffer = Buffer.from(await imageResponse.arrayBuffer());
|
|
fs.writeFileSync('product-no-bg.png', buffer);
|
|
console.log('Saved to: product-no-bg.png');
|
|
|
|
return data.data[0].url;
|
|
}
|
|
|
|
// =============================================================================
|
|
// INPUT FIDELITY OPTIONS
|
|
// =============================================================================
|
|
|
|
async function fidelityComparison() {
|
|
const baseFormData = () => {
|
|
const formData = new FormData();
|
|
formData.append('model', 'gpt-image-1');
|
|
formData.append('image', fs.createReadStream('./portrait.jpg'));
|
|
formData.append('prompt', 'Add sunglasses to the person');
|
|
return formData;
|
|
};
|
|
|
|
// Low fidelity (more creative freedom)
|
|
const lowFidelity = baseFormData();
|
|
lowFidelity.append('input_fidelity', 'low');
|
|
|
|
const lowResponse = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...lowFidelity.getHeaders(),
|
|
},
|
|
body: lowFidelity,
|
|
});
|
|
|
|
const lowData: any = await lowResponse.json();
|
|
console.log('Low fidelity URL:', lowData.data[0].url);
|
|
|
|
// High fidelity (stay closer to original)
|
|
const highFidelity = baseFormData();
|
|
highFidelity.append('input_fidelity', 'high');
|
|
|
|
const highResponse = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...highFidelity.getHeaders(),
|
|
},
|
|
body: highFidelity,
|
|
});
|
|
|
|
const highData: any = await highResponse.json();
|
|
console.log('High fidelity URL:', highData.data[0].url);
|
|
|
|
return { low: lowData.data[0].url, high: highData.data[0].url };
|
|
}
|
|
|
|
// =============================================================================
|
|
// OUTPUT FORMATS AND COMPRESSION
|
|
// =============================================================================
|
|
|
|
async function formatComparison() {
|
|
const basePrompt = 'Add a blue sky to the background';
|
|
|
|
// PNG (supports transparency, larger file)
|
|
const pngFormData = new FormData();
|
|
pngFormData.append('model', 'gpt-image-1');
|
|
pngFormData.append('image', fs.createReadStream('./scene.jpg'));
|
|
pngFormData.append('prompt', basePrompt);
|
|
pngFormData.append('format', 'png');
|
|
|
|
const pngResponse = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...pngFormData.getHeaders(),
|
|
},
|
|
body: pngFormData,
|
|
});
|
|
|
|
const pngData: any = await pngResponse.json();
|
|
console.log('PNG format URL:', pngData.data[0].url);
|
|
|
|
// JPEG (smaller file, no transparency)
|
|
const jpegFormData = new FormData();
|
|
jpegFormData.append('model', 'gpt-image-1');
|
|
jpegFormData.append('image', fs.createReadStream('./scene.jpg'));
|
|
jpegFormData.append('prompt', basePrompt);
|
|
jpegFormData.append('format', 'jpeg');
|
|
jpegFormData.append('output_compression', '80'); // 0-100
|
|
|
|
const jpegResponse = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...jpegFormData.getHeaders(),
|
|
},
|
|
body: jpegFormData,
|
|
});
|
|
|
|
const jpegData: any = await jpegResponse.json();
|
|
console.log('JPEG format URL:', jpegData.data[0].url);
|
|
|
|
// WebP (best compression, supports transparency)
|
|
const webpFormData = new FormData();
|
|
webpFormData.append('model', 'gpt-image-1');
|
|
webpFormData.append('image', fs.createReadStream('./scene.jpg'));
|
|
webpFormData.append('prompt', basePrompt);
|
|
webpFormData.append('format', 'webp');
|
|
webpFormData.append('output_compression', '85');
|
|
|
|
const webpResponse = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...webpFormData.getHeaders(),
|
|
},
|
|
body: webpFormData,
|
|
});
|
|
|
|
const webpData: any = await webpResponse.json();
|
|
console.log('WebP format URL:', webpData.data[0].url);
|
|
|
|
return { png: pngData.data[0].url, jpeg: jpegData.data[0].url, webp: webpData.data[0].url };
|
|
}
|
|
|
|
// =============================================================================
|
|
// COMMON EDITING TASKS
|
|
// =============================================================================
|
|
|
|
async function commonEdits() {
|
|
// 1. Color correction
|
|
const colorCorrect = new FormData();
|
|
colorCorrect.append('model', 'gpt-image-1');
|
|
colorCorrect.append('image', fs.createReadStream('./photo.jpg'));
|
|
colorCorrect.append('prompt', 'Increase brightness and saturation, make colors more vibrant');
|
|
|
|
// 2. Object removal
|
|
const objectRemoval = new FormData();
|
|
objectRemoval.append('model', 'gpt-image-1');
|
|
objectRemoval.append('image', fs.createReadStream('./scene.jpg'));
|
|
objectRemoval.append('prompt', 'Remove the person from the background');
|
|
|
|
// 3. Style transfer
|
|
const styleTransfer = new FormData();
|
|
styleTransfer.append('model', 'gpt-image-1');
|
|
styleTransfer.append('image', fs.createReadStream('./photo.jpg'));
|
|
styleTransfer.append('prompt', 'Transform this photo into a watercolor painting style');
|
|
|
|
// 4. Add text/overlay
|
|
const addText = new FormData();
|
|
addText.append('model', 'gpt-image-1');
|
|
addText.append('image', fs.createReadStream('./poster.jpg'));
|
|
addText.append('prompt', 'Add the text "SALE" in large bold letters at the top');
|
|
|
|
console.log('Common editing tasks prepared');
|
|
|
|
return { colorCorrect, objectRemoval, styleTransfer, addText };
|
|
}
|
|
|
|
// =============================================================================
|
|
// ERROR HANDLING
|
|
// =============================================================================
|
|
|
|
async function withErrorHandling() {
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('model', 'gpt-image-1');
|
|
formData.append('image', fs.createReadStream('./input.jpg'));
|
|
formData.append('prompt', 'Edit the image');
|
|
|
|
const response = await fetch('https://api.openai.com/v1/images/edits', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
|
...formData.getHeaders(),
|
|
},
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(`API error: ${error.error?.message}`);
|
|
}
|
|
|
|
const data: any = await response.json();
|
|
return data.data[0].url;
|
|
} catch (error: any) {
|
|
if (error.message.includes('file not found')) {
|
|
console.error('Input image file not found');
|
|
} else if (error.message.includes('rate limit')) {
|
|
console.error('Rate limit exceeded - wait and retry');
|
|
} else {
|
|
console.error('Unexpected error:', error.message);
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// MAIN EXECUTION
|
|
// =============================================================================
|
|
|
|
async function main() {
|
|
console.log('=== OpenAI Image Editing (GPT-Image-1) Examples ===\n');
|
|
|
|
console.log('Note: This script requires input images to run.');
|
|
console.log('Create test images first or modify the file paths.\n');
|
|
|
|
// Uncomment the examples you want to run:
|
|
|
|
// console.log('1. Basic Edit:');
|
|
// await basicEdit();
|
|
// console.log();
|
|
|
|
// console.log('2. Composite Images:');
|
|
// await compositeImages();
|
|
// console.log();
|
|
|
|
// console.log('3. Remove Background:');
|
|
// await removeBackground();
|
|
// console.log();
|
|
}
|
|
|
|
// Run if executed directly
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
export {
|
|
basicEdit,
|
|
compositeImages,
|
|
removeBackground,
|
|
fidelityComparison,
|
|
formatComparison,
|
|
commonEdits,
|
|
withErrorHandling,
|
|
};
|