Files
gh-jezweb-claude-skills-ski…/templates/image-editing.ts
2025-11-30 08:25:12 +08:00

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,
};